MyBatis Plus @DS注解原理
所属分类 mybatis-plus
浏览量 23
在 MyBatis Plus 中,@DS 注解的实现原理依赖于 AOP(面向切面编程) 和 动态数据源切换机制,
其核心目标是通过注解标记,实现对不同数据源的自动切换
AOP 拦截:通过注解解析和 AOP 切面,动态设置当前线程的数据源
动态数据源代理:通过 AbstractRoutingDataSource 实现数据源的动态切换
上下文管理:使用 ThreadLocal 保证线程内的数据源隔离和一致性
一、核心组件与流程
@DS 注解
用于标记在 类 或 方法 上,指定当前操作使用的数据源名称
@DS("slave1")
public void queryData() {
// 查询操作,默认使用 slave1 数据源
}
AOP 切面(DynamicDataSourceAspect)
MyBatis Plus 提供了 DynamicDataSourceAspect 类,通过 AOP 拦截带有 @DS 注解的方法
切点逻辑:拦截所有带有 @DS 注解的方法或类
环绕通知:在方法执行前解析注解,确定使用哪个数据源,并将数据源名称存储到 ThreadLocal 中
动态数据源代理(DynamicDataSource)
MyBatis Plus 使用 DynamicDataSource 作为数据源的代理类
该类继承自 AbstractRoutingDataSource,通过重写 determineCurrentLookupKey() 方法,
从 ThreadLocal 中获取当前线程的数据源名称,并返回对应的物理数据源
ThreadLocal 上下文
使用 ThreadLocal 存储当前线程的数据源名称,确保每个线程的数据源隔离
示例代码(简化版):
public class DynamicDataSourceContextHolder {
private static final ThreadLocal< String> CONTEXT_HOLDER = new ThreadLocal< >();
public static void setDataSource(String ds) {
CONTEXT_HOLDER.set(ds);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
二、实现步骤详解
1. 注解解析
注解优先级:
方法上的 @DS 注解 > 类上的 @DS 注解 > 全局默认数据源(spring.datasource.dynamic.primary)
AOP 拦截逻辑:
DynamicDataSourceAspect 通过 @annotation(ds) 或 @within(ds) 匹配带有 @DS 注解的方法或类
在环绕通知中,从注解中提取数据源名称,并调用 DynamicDataSourceContextHolder.setDataSource(ds) 设置当前线程的数据源
2. 数据源切换
动态数据源代理:
DynamicDataSource 继承自 AbstractRoutingDataSource,其核心方法:
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSource(); // 从 ThreadLocal 获取数据源名称
}
该方法决定了当前线程使用的实际数据源
3. SQL 执行
数据源绑定:
MyBatis Plus 在执行 SQL 时,会从 DynamicDataSource 获取当前线程绑定的数据源,确保所有操作(如 select、insert)使用正确的数据库连接
事务管理:
如果方法上有 @Transactional 注解,Spring 会使用当前线程绑定的数据源开启事务。若涉及多个数据源,需引入分布式事务框架(如 Seata)
4. 上下文清理
线程结束时:
在 AOP 的 after 或 finally 块中,
调用 DynamicDataSourceContextHolder.clearDataSource() 清除 ThreadLocal 中的数据源,避免内存泄漏和线程复用问题
三、配置与使用示例
1. 配置多数据源
spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: root
slave1:
url: jdbc:mysql://localhost:3306/slave1_db
username: root
password: root
2. 使用 @DS 注解
@Service
public class UserService {
@DS("master") // 保存操作使用 master 数据源
public void saveUser(User user) {
userMapper.insert(user);
}
@DS("slave1") // 查询操作使用 slave1 数据源
public User getUserById(Long id) {
return userMapper.selectById(id);
}
}
四、关键代码逻辑(简化版)
1. AOP 切面逻辑
@Aspect
@Component
public class DynamicDataSourceAspect {
@Around("@annotation(ds) || @within(ds)")
public Object around(ProceedingJoinPoint point, DS ds) throws Throwable {
String dataSource = ds.value(); // 从注解中获取数据源名称
DynamicDataSourceContextHolder.setDataSource(dataSource); // 存入 ThreadLocal
try {
return point.proceed(); // 执行目标方法
} finally {
DynamicDataSourceContextHolder.clearDataSource(); // 清理上下文
}
}
}
2. 动态数据源代理
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSource(); // 返回当前线程的数据源名称
}
}
五、注意事项
线程安全问题:
ThreadLocal 中的数据源绑定需在当前线程内有效,避免在异步任务中直接使用(需手动传递上下文)
事务与数据源切换:
若方法涉及多个数据源操作,需使用分布式事务框架(如 Seata)
嵌套方法调用:
内部方法调用时,若未显式指定 @DS 注解,会继承外层方法的数据源
上一篇
下一篇
词向量 Word Embedding
spring事务的一些知识点
MyBatis Plus 动态数据源注解 @DS 注解
多个AOP切面注解 执行顺序说明