要实现基于注解的动态数据源切换,需四步:一定义@datasource注解并配置runtime保留策略和method/type作用域;二用threadlocal实现dynamicdatasourcecontextholder管理当前线程数据源key;三继承abstractroutingdatasource重写determinecurrentlookupkey()从上下文中获取key;四通过@aspect切面在方法执行前设置、执行后清除数据源,确保线程安全。

自定义注解标记数据源
要实现基于注解的动态数据源切换,第一步是定义一个自定义注解,比如 @DataSource,用于标识方法或类应使用的数据源名称。
注解需支持在运行时通过反射读取,因此 @Retention(RetentionPolicy.RUNTIME) 是必须的;同时通常作用于方法和类上,所以加上 @Target({ElementType.METHOD, ElementType.TYPE})。
示例定义:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "master"; // 默认走主库
}
动态数据源上下文管理器
核心是用 ThreadLocal 维护当前线程绑定的数据源 key,避免多线程干扰。提供 set、get、clear 三个基础操作。
常见写法如下:
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<string> CONTEXT_HOLDER = new ThreadLocal();
public static void setDataSource(String dataSourceName) {
CONTEXT_HOLDER.set(dataSourceName);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
</string>
继承 AbstractRoutingDataSource 实现路由逻辑
Spring 的 AbstractRoutingDataSource 是实现动态数据源的关键抽象类。它在每次获取连接前调用 determineCurrentLookupKey() 方法,返回一个 key,再根据该 key 从配置好的 targetDataSources 中查找对应数据源。
你需要重写这个方法,让它从上面的上下文管理器中读取当前线程指定的数据源名:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSource();
}
}
然后在 Spring 配置中把多个真实数据源(如 master、slave1、slave2)注入到 targetDataSources,并设置一个 defaultTargetDataSource(通常是 master)。
切面拦截注解并切换上下文
使用 @Aspect 切面,在目标方法执行前读取 @DataSource 注解值,并设入上下文;方法结束后清除,防止污染后续调用。
关键点:
- 切点表达式匹配带该注解的方法(含类级别继承逻辑)
- 优先取方法上的注解,未标注则回退到类上
- 务必在 finally 块中 clear,确保线程安全
@Aspect
@Component
public class DataSourceAspect {
@Around("@annotation(ds) || @within(ds)")
public Object switchDataSource(ProceedingJoinPoint pjp, DataSource ds) throws Throwable {
String dataSourceName = ds.value();
DynamicDataSourceContextHolder.setDataSource(dataSourceName);
try {
return pjp.proceed();
} finally {
DynamicDataSourceContextHolder.clearDataSource();
}
}
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











