产生原因
- 代理模式中真实对象方法调用方法不会走代理对象,因此类似于@DS注解使用AOP切面进行数据源的切换,在方法调用方法是不会成功的
解决办法
- 原理:通过修改本地线程变量中的数据源栈,来进行数据源的切换
- 本实例使用@DS,其他动态数据源(自定义动态数据源)逻辑类似
- 封装的手动切换数据源工具类
自定义一个SmallConsumer注解,主要是填充无参回调函数接口
@FunctionalInterface
public interface SmallConsumer {
/**
* 接收方法
*/
void accept();
/**
* 用于递归调用
* @param after
* @return
*/
default SmallConsumer andThen(SmallConsumer after) {
Objects.requireNonNull(after);
return () -> {
accept();
after.accept();
};
}
}
编写手动切换数据源工具类,实际就是操作本地线程变量
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
@Component
@Slf4j
public class ChooseDsUtil {
private final static String masterDB = "master";
private final static String slaveDB = "slave";
@Resource
private ApplicationContext applicationContext;
/**
* 手动切换到master执行
* 例子:chooseDsUtil.masterExec(() -> memberDao.insert(userPo));
* @param execConsumer
*/
public void masterExec(SmallConsumer execConsumer) {
try {
DynamicDataSourceContextHolder.push(masterDB);
execConsumer.accept();
} finally {
DynamicDataSourceContextHolder.poll();
}
}
/**
* 手动切换到slave执行
* 例子:chooseDsUtil.slaveExec(() -> getUserById("1"));
* @param execConsumer
*/
public void slaveExec(SmallConsumer execConsumer) {
try {
DynamicDataSourceContextHolder.push(slaveDB);
execConsumer.accept();
} finally {
DynamicDataSourceContextHolder.poll();
}
}
/**
* 使用AOP的方式进行自动切换,通过获取aop的bean来防止方法调用方法而跳过aop
* 例子:chooseDsUtil.getAopBean(this.getClass()).B(userPo);,使用B方法的@DS,进行数据源自动切换
* @return
*/
public <T> T getAopBean(Class<T> tClass) {
return applicationContext.getBean(tClass);
}
}
- 注意:不管是自动切换数据源还是手动切换数据源,切换执行完毕后都不会自动切换回来,比如:在更新逻辑中,先切换到读库进行数据获取,然后再数据处理,最后将处理后的数据更新到写库中,此时在写之前需要切换数据源到写库