在 Spring Boot 生态中,dynamic-datasource-spring-boot-starter 是一个非常实用的组件,它为我们在多数据源场景下 提供了便捷的解决方案。在上一篇文章《一分钟上手:如何创建你的第一个 Spring Boot Starter》中,我们学习了如何创建自己的 starter,今天我们就来深入探究下 dynamic-datasource-spring-boot-starter 的源码,了解它是如何实现动态数据源切换功能的。
一、dynamic-datasource-spring-boot-starter介绍
dynamic-datasource-spring-boot-starter 是一个用于在 Spring Boot 项目中实现动态数据源切换的工具。
在实际的应用开发中,经常会遇到需要连接多个数据源的情况,例如一个销售系统会根据不同的业务模块,如线索、订单、库存、物流等连接到不同的数据库。手动管理多个数据源的切换和配置是一项复杂且容易出错的任务,而这个 starter 就是为了解决这些问题而生。
二、依赖的jar包
1. SpringBoot相关依赖
提供了JDBC数据库连接操作、AOP(面向切面编程)、Actuator应用监控、Web应用开发等功能,Actuator和Web依赖都是可选的,不是必须的。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
2. MyBatis-Plus
增强版的MyBatis框架,可以极大简化数据库操作,业务的mapper层只需要继承BaseMapper,即可使用mybatis-plus提供的增删改查的API 。使开发者能够更加专注于业务逻辑的实现,而无需在复杂的 SQL 编写和数据库连接管理上耗费过多精力。该jar包也是可选的,不是必须的。
3. 连接池
HikariCP、Druid、Beecp、Commons DBCP2等用于管理数据库连接。
这些jar包也都是可选的 ,后端开发使用的最多的还是Druid数据库连接池。
xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java7</artifactId>
<version>${hikariCp.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.chris2018998</groupId>
<artifactId>beecp</artifactId>
<version>${beeCp.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>${dbcp2.version}</version>
<optional>true</optional>
</dependency>
4. 配置处理器
在开发阶段为 IDE 提供辅助功能,如代码提示和验证配置属性的正确性 。在项目的实际运行时,并不需要这个依赖来执行应用程序的核心逻辑,所以也将其设置为可选,避免了不必要的依赖传递,减小了项目最终打包的体积。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
5. P6Spy
用于监控和调试SQL语句。可能在开发和测试环境中非常有用,但在生产环境中可能不需要,所以业将其标记为可选依赖,让使用方根据实际情况决定是否引入。
6. Seata
提供分布式事务解决方案,根据项目实际需要决定要不要引入,所以该jar包也是可选的。
三、DynamicDataSourceAutoConfiguration类剖析
DynamicDataSourceAutoConfiguration类是dynamic-datasource-spring-boot-starter包的自动配置类,整个动态数据源的配置和初始化都在这里。
java
@Configuration
@EnableConfigurationProperties({DynamicDataSourceProperties.class})
@AutoConfigureBefore(
value = {DataSourceAutoConfiguration.class},
name = {"com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure"}
)
@Import({DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class, DynamicDataSourceHealthCheckConfiguration.class})
@ConditionalOnProperty(
prefix = "spring.datasource.dynamic",
name = {"enabled"},
havingValue = "true",
matchIfMissing = true
)
public class DynamicDataSourceAutoConfiguration {
......
}
这种配置类相关的注解之前的博客也分享过,这里就不再赘述了,我们重点来剖析下该配置类中的几个关键方法。
1. dynamicDataSourceProvider方法
用于创建动态数据源提供者 DynamicDataSourceProvider。其主要职责是根据配置文件中的数据源信息构建数据源对象,并将其提供给动态数据源路由机制。
java
@Bean
@ConditionalOnMissingBean
// 确保Spring容器中不存在 DynamicDataSourceProvider 类型的 Bean 时才创建该 Bean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
Map<String, DataSourceProperty> datasourceMap = this.properties.getDatasource();
return new YmlDynamicDataSourceProvider(datasourceMap);
}
2. dataSource方法
用于是创建动态数据源 DynamicRoutingDataSource。它整合了各种配置信息,包括默认数据源、严格模式、切换策略、数据源提供者以及与SQL语句监控和分布式事务相关的配置,构建出一个完整的动态数据源对象,使其能够在运行时根据不同条件动态切换数据源。
java
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
// 默认数据源
dataSource.setPrimary(this.properties.getPrimary());
// 根据配置决定在数据源切换时是否进行严格检查,以确保数据源的正确性和一致性。
dataSource.setStrict(this.properties.getStrict());
// 数据源切换策略,如基于注解、基于配置文件或基于特定规则的切换策略
dataSource.setStrategy(this.properties.getStrategy());
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(this.properties.getP6spy());
dataSource.setSeata(this.properties.getSeata());
return dataSource;
}
3. dynamicDatasourceAnnotationAdvisor 方法
负责创建动态数据源注解切面的Advisor。用于在方法执行时根据注解信息进行数据源切换。
java
@Role(2)
@Bean
public Advisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor(this.properties.isAllowedPublicOnly(), dsProcessor);
DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
advisor.setOrder(this.properties.getOrder());
return advisor;
}
4. dynamicTransactionAdvisor 方法
用于创建动态事务 Advisor。它定义了一个基于注解的事务切面,用于在方法执行时管理事务,确保数据库操作的原子性、一致性、隔离性和持久性。
java
@Role(2)
@ConditionalOnProperty(
prefix = "spring.datasource.dynamic",
name = {"seata"},
havingValue = "false",
matchIfMissing = true
)
@Bean
public Advisor dynamicTransactionAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("@annotation(com.baomidou.dynamic.datasource.annotation.DSTransactional)");
return new DefaultPointcutAdvisor(pointcut, new DynamicLocalTransactionAdvisor());
}
5. dsProcessor 方法
用于创建数据源处理器 DsProcessor。数据源处理器负责根据不同的条件(如请求头、会话信息、SpEL 表达式等)确定要切换的数据源,是实现动态数据源切换逻辑的关键组件之一。
自动配置类剖析完了,剩下的就是配置属性类DynamicDataSourceProperties和spring.factories 文件了。
java
@ConfigurationProperties(
prefix = "spring.datasource.dynamic"
)
public class DynamicDataSourceProperties {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceProperties.class);
public static final String PREFIX = "spring.datasource.dynamic";
public static final String HEALTH = "spring.datasource.dynamic.health";
public static final String DEFAULT_VALID_QUERY = "SELECT 1";
private String primary = "master";
private Boolean strict = false;
private Boolean p6spy = false;
private Boolean seata = false;
private Boolean lazy = false;
private SeataMode seataMode;
private boolean health;
private String healthValidQuery;
private Map<String, DataSourceProperty> datasource;
private Class<? extends DynamicDataSourceStrategy> strategy;
private Integer order;
@NestedConfigurationProperty
private DruidConfig druid;
@NestedConfigurationProperty
private HikariCpConfig hikari;
@NestedConfigurationProperty
private BeeCpConfig beecp;
@NestedConfigurationProperty
private Dbcp2Config dbcp2;
private String publicKey;
private boolean allowedPublicOnly;
......
}
DynamicDataSourceProperties一般都配置在Apollo中。
以下是spring.factories文件。
xml
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
四、总结
通过对 dynamic-datasource-spring-boot-starter 的剖析,我们可以了解到它是实现动态数据源切换的机制。这一组件为开发者在处理多数据源场景时提供了极大的便利,使得应用的可扩展性和可维护性得到了显著提升。
下一篇博客我们将结合最佳实践来揭秘dynamic-datasource-spring-boot-starter组件的使用。