首页  

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切面注解 执行顺序说明