在平时开发过程中,很多内部的项目都是直接访问多个数据库,这样平时一个项目一个数据库就不够用了,spring支持多数据源。
现在很流行Spring boot自动配置,我在这里分享一个基于Spring boot starter方式的多数据源(动态自动切换)整合方案。
由于我们需要做一个starter,所以我们参照mybatis-spring-boot-start的方式新建两个工程:
1、创建 dynamic-datasource-spring-boot-autoconfigure
pom.xml
1 |
|
因为我们是一个spring boot项目,所以我们这里需要继承spring boot的parent,然后依赖spring-boot-starter,
和jdbc starter(数据源)、aop starter(切面拦截自动切换数据源)、configuration-processor(为了把实体类的属性在properties中配置的时候可以提示)
DynamicDataSourceProperties 配置类
1 | "dynamic.ds") (prefix = |
定义配置属性自动配置类,前缀为dynamic.ds
, 为了简化操作,我们这里数据源的配置直接使用的是Spring中的DataSourceProperties
,我们配置类中主要的分为:
1、datasource 配置多个数据源以Key-Value形式,key为数据源名称,Value为DataSourceProperties
,例如:dynamic.ds.datasource.xxx.url 其中的xxx为key,url为DataSourceProperties中的属性
2、defaultDataSource 默认数据源,如果配置了spring.datasource.xxx
则使用该数据源为默认数据源。
3、notFoundUseDefault 未找到指定数据源是否使用默认数据源,如果使用了一个不存在(没有在第1点中配置的)的数据源,是否使用默认数据源。
@Data 这个注解为lombok中的,主要是自动生成属性的setter和getter。@ConfigurationProperties 为spring中的属性配置注解,会自动把properties中相匹配的属性注入。
DynamicDataSource 继承 AbstractRoutingDataSource
1 | public class DynamicDataSource extends AbstractRoutingDataSource { |
Spring JDBC 包中有一个AbstractRoutingDataSource
类,该类为数据源的路由抽象类,
我们这里主要使用determineCurrentLookupKey
这个方法,返回一个数据源名称即可。
DynamicDataSourceAutoConfiguration 自动初始化配置
1 |
|
@Configuration 注解,告诉spring该类是一个配置类,@ConditionalOnClass(DataSource.class) 表示在含有DataSource这个类的情况下有效。
@EnableConfigurationProperties 启用属性配置,@AutoConfigureBefore 表示在某个类初始化之前,这里为DataSourceAutoConfiguration。
@Import 导入其他需要配置的类。
该类实现了EnvironmentAware接口,用于获取用户是否有配置spring.datasource前缀的属性。如果用户没有配置默认数据源,并且配置了spring.datasource则使用该数据源为默认数据源。
构造函数很简单,主要是把配置的数据源初始化,没什么可以讲的。
然后使用@Bean注解暴露了一个DataSource,就是我们上面的DynamicDataSource,这里主要是把我们的动态数据源放入spring容器,并设置数据源集合以及默认数据源,这样在spring的jdbc流程中可以直接获取到该数据源。
DynamicDataSourceContextHolder
1 | public class DynamicDataSourceContextHolder { |
这里主要是利用ThreadLocal的特性,把数据源标示保存在当前线程中,避免多线程操作数据源时互相干扰。
TargetDataSource 注解
1 | ({ ElementType.METHOD, ElementType.TYPE }) |
这个注解主要是配合aop切面自动切换数据源使用的,和@Transaction同理。
DynamicDataSourceAop 动态数据源切面
1 |
|
这里的@Order必须要比spring事务的order要小,也就是说该切面必须比spring的事务先执行,否则无效。
我们这里使用了两个@Around环绕通知,一个拦截方法上含有TargetDataSource注解的,另一个拦截方法的目标类上含有TargetDataSource注解的。
在调用目标方法之前把需要的数据源标示保存到当前线程中,然后执行目标方法。最后别忘记要把当前线程清空,否则会导致内存溢出。
写到这里,我们的动态数据源的配置类就已经写完了,但是这个时候spring并不认识我们,所以我们需要告诉它:
在resource目录新建META-INF目录并且新建spring.factories文件,内容如下:1
2
3# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alone.spring.boot.autoconfigure.dynamic.datasource.DynamicDataSourceAutoConfiguration
告诉spring我们的DynamicDataSourceAutoConfiguration类为自动配置类。
2、创建 dynamic-datasource-spring-boot-starter
这个工程可要可不要,这里主要是为了按照spring boot starter的规范,所以才分为了两个工程,如果你嫌麻烦可以不要这个工程即可。
该工程不需要resource以及class目录,只需要pom中依赖第一步的工程即可,并且该工程不需要继承spring boot parent。
测试
新建测试工程 dynamic-datasource-demo
pom.xml
1 |
|
依赖,我们的dynamic-datasource-spring-boot-starter,并且依赖数据驱动包,我这里用的mysql。
创建 测试服务 DemoService
1 |
|
第一个 defaultMethod 为默认数据源,第二个我们在方法上添加了@TargetDataSource注解并且赋值为other数据源。
application.properties
1 | dynamic.ds.datasource.other.url = jdbc:mysql://url/other?characterEncoding=utf8&useSSL=false&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true |
大功告成。
全部代码在Github: dynamic-datasource-spring-boot-starter、dynamic-datasource-spring-boot-autoconfigure,
也可以直接使用jitpack源:,如果你使用了私服仓库请配置你的setting.xml中的mirrorOf为:*,!jitpack.io