Java多数据源自动切换通过自定义@DataSource注解+AOP切面+DynamicDataSource实现:注解标记数据源,AOP在方法执行前设置ThreadLocal中的数据源类型,DynamicDataSource重写determineCurrentLookupKey()动态路由。

Java中用注解实现多数据源自动切换,核心是结合自定义注解 + AOP切面 + 动态数据源(AbstractRoutingDataSource)完成运行时路由。下面以 Spring Boot 项目为例,说明关键步骤和代码结构。
1. 定义自定义注解 @DataSource
用于标记方法或类,指定要使用的数据源名称:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "master"; // 默认走主库
}
2. 实现动态数据源路由逻辑
继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey() 方法,从线程上下文(如 ThreadLocal)读取当前数据源标识:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
配套的上下文工具类:
立即学习“Java免费学习笔记(深入)”;
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
3. 编写AOP切面拦截 @DataSource 注解
在方法执行前设置数据源类型,方法结束后清除,避免线程复用导致污染:
@Aspect
@Component
@Order(-1) // 确保优先于事务切面执行
public class DataSourceAspect {
@Before("@annotation(dataSource)")
public void beforeSwitchDataSource(JoinPoint point, DataSource dataSource) {
String value = dataSource.value();
DataSourceContextHolder.setDataSourceType(value);
}
@After("@annotation(dataSource)")
public void afterClearDataSource(JoinPoint point, DataSource dataSource) {
DataSourceContextHolder.clearDataSourceType();
}
}
4. 配置多个数据源并注入 DynamicDataSource
在 @Configuration 类中配置 master/slave 数据源,并组装成 DynamicDataSource:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slaveDataSource());
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource()); // 默认主库
return dynamicDataSource;
}
}
同时在 application.yml 中配置两个数据源:
spring:
datasource:
master:
url: jdbc:mysql://localhost:3306/db_master
username: root
password: 123
slave:
url: jdbc:mysql://localhost:3306/db_slave
username: root
password: 123
使用时只需在 Service 方法上加注解即可:
@Service
public class UserService {
@DataSource("slave")
public List<User> findAllReadonly() {
return userMapper.selectAll();
}
// 不加注解则走默认 master
public void createUser(User user) {
userMapper.insert(user);
}
}
注意:该方案要求事务管理器也基于 DynamicDataSource 创建;若方法有 @Transactional,需确保 AOP 切面顺序早于事务切面(用 @Order 控制),否则事务可能已启动才切换数据源,导致失效。


















