SpringBoot中Druid连接池与多数据源切换的方法
作者:码到三十五
微服务架构中多数据源切换是个常见的需求,Spring Boot 提供了强大的支持来简化这一过程.本文给大家介绍了SpringBoot中Druid连接池与多数据源切换的方法,需要的朋友可以参考下
多数据源切换原理
多数据源切换的原理主要基于 Spring 的 AbstractRoutingDataSource 类。AbstractRoutingDataSource 类允许根据运行时上下文动态选择数据源。其核心在于实现 determineCurrentLookupKey 方法,该方法决定当前操作使用哪个数据源。 AbstractRoutingDataSource 实现多数据源切换的原理:
1. 数据源映射
AbstractRoutingDataSource
内部维护了一个映射(Map),用于存储数据源标识(key)和对应的数据源实例(value)。这个映射允许根据数据源标识快速查找和获取对应的数据源。
2. 数据源标识的确定
AbstractRoutingDataSource
提供了一个抽象方法determineCurrentLookupKey()
,该方法用于确定当前需要使用的数据源标识。这个方法需要由子类实现,以返回当前线程或请求应该使用的数据源标识。
3. 数据源的选择与连接获取
- 当应用程序需要获取数据库连接时,
AbstractRoutingDataSource
的getConnection()
方法会被调用。这个方法首先调用determineCurrentLookupKey()
方法来获取当前的数据源标识,然后根据这个标识从内部映射中查找对应的数据源。 - 一旦找到了对应的数据源,
AbstractRoutingDataSource
就会调用该数据源的getConnection()
方法来获取实际的数据库连接,并将这个连接返回给应用程序。
4. 数据源切换的实现
- 为了实现数据源的动态切换,通常会在子类中重写
determineCurrentLookupKey()
方法,并根据当前的上下文(如线程变量)来确定返回的数据源标识。 - 此外,通常会使用
ThreadLocal
来存储每个线程的数据源标识,这样每个线程都可以独立地切换数据源而不会互相干扰。
实现步骤
1. 依赖
引入 MySQL 和 Druid 的依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency>
2. 配置数据源
在 application.yml
文件中配置多个数据源。
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: datasource1: url: jdbc:mysql://localhost:3306/master_db username: root password: password initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 datasource2: url: jdbc:mysql://localhost:3306/slave_db username: root password: password initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000
3. 创建数据源配置类
创建一个配置类来定义数据源 Bean。
@Configuration public class DataSourceConfig { @Bean @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "datasource1") @ConfigurationProperties(prefix = "spring.datasource.druid.datasource1") public DataSource dataSource1() { return DruidDataSourceBuilder.create().build(); } @Bean @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "datasource2") @ConfigurationProperties(prefix = "spring.datasource.druid.datasource2") public DataSource dataSource2() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setDefaultTargetDataSource(dataSource1()); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("dataSource1", dataSource1()); targetDataSources.put("dataSource2", dataSource2()); dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } }
4. 实现 AbstractRoutingDataSource
创建一个继承自 AbstractRoutingDataSource
的类,并实现 determineCurrentLookupKey
方法。
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } }
5. 创建 DataSourceContextHolder
创建一个工具类来保存当前线程的数据源信息。
public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clearDataSource() { CONTEXT_HOLDER.remove(); } }
6. AOP动态切换数据源
使用 AOP 在方法执行前后切换数据源。
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { // 方法或者类上的横切点 @Pointcut("@annotation(dataSource) || @within(dataSource)") public void dataSourcePointcut(DataSource dataSource) {} @Before("dataSourcePointcut(dataSource)") public void switchDataSource(JoinPoint joinPoint, DataSource dataSource) { // 从注解中获取数据源标识 String dataSourceKey = dataSource.value(); // 切换到指定的数据源 DataSourceContextHolder.setDataSourceType(dataSourceKey); } @AfterReturning(pointcut = "dataSourcePointcut(dataSource)", returning = "result") public void restoreDataSource(JoinPoint joinPoint, DataSource dataSource, Object result) { // 恢复默认数据源(可选) DataSourceContextHolder.clearDataSourceType(); } } /** * 自定义注解 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default "dataSource1"; }
7. 使用自定义注解
在需要切换数据源的方法上使用自定义注解。
注意
- 数据源切换的逻辑应该尽可能简单和高效,以避免对应用程序性能产生负面影响。
- 在切换数据源时,需要注意事务管理的问题,确保在同一个事务中只使用同一个数据源。
到此这篇关于SpringBoot中Druid连接池与多数据源切换的方法的文章就介绍到这了,更多相关SpringBoot Druid与数据源切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!