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导致异常 |
性能影响 | 切换本身性能开销小,但频繁切换可能影响可读性和调试 |
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。