springboot多数据源的原理
下文笔者讲述SpringBoot多数据源的原理简介说明,如下所示
SpringBoot多数据源原理
Spring Boot多数据源原理
主要依赖于
动态数据源切换机制
和 自定义 `AbstractRoutingDataSource`
多数据源的具体实现思路:
根据业务逻辑在运行时选择不同的数据源
SpringBoot多数据源核心原理
1.`AbstractRoutingDataSource`
- Spring提供抽象类 `AbstractRoutingDataSource`
它是一个基于 lookup key 动态决定使用哪个目标数据源的路由类。
- 它内部维护了一个`targetDataSources`
映射(`Map<Object, DataSource>`)
通过一个`determineCurrentLookupKey()`方法
来决定当前线程使用数据源key
public abstract class AbstractRoutingDataSource extends AbstractDataSource {
private Map<Object, DataSource> targetDataSources;
private DataSource defaultTargetDataSource;
protected abstract Object determineCurrentLookupKey();
}
2.使用 ThreadLocal 存储当前线程的数据源标识
- 为了实现多线程环境下的数据源隔离
通常会用`ThreadLocal`来保存当前线程的数据源标识(如 `master`, `slave1`, `slave2`)
- 在 AOP 或拦截器中设置该标识
在`determineCurrentLookupKey()`中读取
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceType(String type) {
CONTEXT_HOLDER.set(type);
}
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
3.配置多个数据源 Bean
- 手动配置多个 `DataSource` Bean
并将它们注入到 `AbstractRoutingDataSource`
的 `targetDataSources` 中。
- 设置默认数据源 `defaultTargetDataSource`。
4. AOP/拦截器控制数据源切换
- 使用切面或拦截器,在进入方法前设置数据源标识。
- 例:
根据注解 `@DataSource("slave1")` 自动切换。
SpringBoot多数据源流程图
1.请求进来
2.AOP 拦截方法,判断是否有 `@DataSource` 注解
3.如果有,
调用`DynamicDataSourceContextHolder.setDataSourceType("xxx")`
4.Spring调用
`determineCurrentLookupKey()`获取当前数据源 key
5.根据key
从`targetDataSources`中获取对应的实际`DataSource`
6.数据库操作使用该数据源执行 SQL
7.请求结束,清理 ThreadLocal 中的数据源标识
例
1.自定义数据源类
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
2.配置类
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource(DataSource masterDataSource, DataSource slave1DataSource) {
DynamicDataSource ds = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave1", slave1DataSource);
ds.setTargetDataSources(targetDataSources);
ds.setDefaultTargetDataSource(masterDataSource); // 默认数据源
return ds;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
3.切面控制切换
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(dataSource)")
public void dataSourcePointCut(DataSourceAnnotation dataSource) {}
@Around("dataSourcePointCut(dataSource)")
public Object around(ProceedingJoinPoint point, DataSourceAnnotation dataSource) throws Throwable {
String dsKey = dataSource.value();
DynamicDataSourceContextHolder.setDataSourceType(dsKey);
try {
return point.proceed();
} finally {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
}
4.自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.Runtime)
public @interface DataSourceAnnotation {
String value();
}
SpringBoot多数据源注意事项
| 事项 | 说明 |
| 事务管理 | 动态数据源不支持跨数据源事务(XA 除外) |
| 线程安全 | 必须使用 ThreadLocal,避免线程复用问题(如线程池) |
| 默认数据源 | 建议设置一个默认数据源,防止未设置key导致异常 |
| 性能影响 | 切换本身性能开销小,但频繁切换可能影响可读性和调试 |
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


