通过自定义注解,AOP,AbstractRoutingDataSource
实现
- 定义
DataSource
注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSource {
String value() default "slave";
}
- 设置AOP:
DynamicDataSourceAspect
动态拦截注解 - 设置数据源
@Aspect
@Component
@Slf4j
public class DynamicDataSourceAspect {
@Before("@annotation(com.linlong.back.core.annotation.DataSource)")
public void beforeSwitch(JoinPoint point) {
//获得当前访问的class
Class<?> clazz = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
// 得到方法的参数的类型
Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DATABASE;
try {
// 获取访问的方法对象
Method method = clazz.getMethod(methodName, argClass);
if (method.isAnnotationPresent(DataSource.class)) {
DataSource annotation = method.getAnnotation(DataSource.class);
dataSource = annotation.value();
}
} catch (NoSuchMethodException e) {
log.error("AOP动态切换数据源异常:", e);
}
DataSourceContextHolder.setDatabase(dataSource);
}
@After("@annotation(com.linlong.back.core.annotation.DataSource)")
public void afterSwitch(JoinPoint point) {
log.info("清空数据源<{}>", DataSourceContextHolder.getDatabase());
DataSourceContextHolder.clear();
}
}
- DataSourceContextHolder 用于保存数据源信息
@Slf4j
public class DataSourceContextHolder {
public static final String DEFAULT_DATABASE = "master";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDatabase(String databaseName) {
log.info("切换到<{}>数据源", databaseName);
contextHolder.set(databaseName);
}
/**
* 获取数据源
*/
public static String getDatabase() {
// 这里也可以直接返回 contextHolder.get(), 在 DynamicDataSource 中指定默认的数据源
return contextHolder.get() == null ? DEFAULT_DATABASE : contextHolder.get();
}
/**
* 清除数据源
*/
public static void clear() {
contextHolder.remove();
}
}
- AbstractRoutingDataSource 抽象路由数据源:决定使用aop中设置数据源
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
log.info("确定当前查找的键<{}>", DataSourceContextHolder.getDatabase());
return DataSourceContextHolder.getDatabase();
}
}
- 数据源设置:设置了默认数据源,所有目标数据源,将此数据源给
SqlSessionFactoryBean
即可
@Primary
@Bean(name = DATA_SOURCE)
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
Map<Object, Object> targetDataSouces = new HashMap<>();
targetDataSouces.put("master", masterDataSource());
targetDataSouces.put("slave", slaveDataSource());
dynamicDataSource.setTargetDataSources(targetDataSouces);
return dynamicDataSource;
}
1. 如何使用?:在 service 中使用注解 @DataScoure("slave")

2. 可能导致的问题?在同一个service中,无法通过 this 调用其他方法(触发aop),事物顺序问题,参考地址:https://blog.csdn.net/dream_broken/article/details/72851329
网友评论