Springboot单个数据源比较容易配置,它自动装配特性支持下,DataSource的初始化和注入等比较便捷。但是在多数据源情况下,反而有些蹩脚;其实实施起来也不算太复杂,方式也比较多。本文展示一个比较易用的方式,仅供参考。
我们假定有2个数据源,dataSource1和dataSource2,基于tomcat-jdbc连接池,粗略配置如下文所示,部分属性可以自行按需增加。
1、application.properties
################# dataSource #################### datasource: default: url: jdbc:mysql://127.0.0.1:3306/test?autoReconnect=false username: root password: root maxActive: 32 maxIdle: 4 minIdle: 1 defaultAutoCommit: true maxAge: 60000 testOnBorrow: true testOnReturn: true validationQuery: SELECT 1 db0: url: jdbc:mysql://127.0.0.1:3306/test0?autoReconnect=false username: root password: root maxActive: 32 maxIdle: 4 minIdle: 1 defaultAutoCommit: true maxAge: 60000 testOnBorrow: true testOnReturn: true validationQuery: SELECT 1 db1: url: jdbc:mysql://127.0.0.1:3306/test1?autoReconnect=false username: root password: root maxActive: 32 maxIdle: 4 minIdle: 1 defaultAutoCommit: true maxAge: 60000 testOnBorrow: true testOnReturn: true validationQuery: SELECT 1
2、Application.java
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @MapperScan("com.vipkid.sample.dao") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @ConfigurationProperties(prefix = "datasource.default") @Bean("defaultDataSource") public DataSource defaultDataSource() { return DataSourceBuilder.create().build(); } @ConfigurationProperties(prefix = "datasource.db0") @Bean(value = "dataSource0") public DataSource dataSource0() { return DataSourceBuilder.create().build(); } @ConfigurationProperties(prefix = "datasource.db1") @Bean(value = "dataSource1") public DataSource dataSource1() { return DataSourceBuilder.create().build(); } }
原理比较简单,对于Spring容器而言,在Bean初始化之前(Spring Bean可以被export之前,并非Bean实例创建之前),可以执行已注册的“postProcessor”来完成一些特定操作。对于SpringBoot环境,如果@Bean被@ConfigurationProperties修饰,那么将会在Bean初始化之前通过"ConfigurationPropertiesBindingPostProcessor"执行一些属性设置操作:根据ConfigurationProperties.prefix通过匹配获得有效的properties列表,依次通过反射机制(内部有复杂的转换)获取同名property的setter方法并执行;最终目的就是将@ConfigurationProperties中限定的属性列表与@Bean实例同名属性进行赋值。
以“defaultDataSource”为例:
1)DataSource defaultDataSource()方法内部,就是尝试加载指定的一些dataSource类型,如classpath中存在则创建相应的DataSource实例。比如我们引入了tomcat-jdbc依赖,那么将会加载“org.apache.tomcat.jdbc.pool.DataSource”并创建其实例。当然你可以强制指定:
return DataSourceBuilder.create() .type(org.apache.tomcat.jdbc.pool.DataSource.class) .build();
此方法返回dataSource实例,但是此实例并没有任何我们所需要的属性。即此时@ConfugurationProperties注释还没有发挥实际作用,只是@Bean部分执行完毕。
2)根据Spring Bean原理,@Bean在export之前,需要执行一些postProcessor;那么在SpringBoot环境下,被@ConfigurationProperties修饰的Bean,需要执行ConfigurationPropertiesBindingPostProcessor,那么在yml中申明的属性才会被bind到DataSource实例中;比如“url”、“maxActive”等等。内部主要是PropertiesConfigurationFactory在发挥作用,将prefix传递给此Factory,此Factory将会从全局的properties列表中匹配符合prefix的属性列表(会准守springboot properties规范,比较将大写与“-”转化等),并对属性依次构建“DataSource”实例的setter方法(反射机制Method),然后执行。
(源码参见,ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(Object bean,String beanName),其中bean为dataSource实例,beanName为“defaultDataSource”)
3)此外,为了避免与SpringBoot默认的dataSource装配冲突,我们需要禁用内置的DataSourceAutoConfiguration:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})