最近整理了一下spring boot關於對多數據源的配置,記錄下來:java
一, 引入Jar包:mysql
<dependency> <!-- MySql驅動 --> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <!-- 鏈接池 --> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency>
二, 配置參數:git
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver druid: one: #數據源1 url: jdbc:mysql://localhost:3306/test1?... username: root password: root two: #數據源2 url: jdbc:mysql://localhost:3306/test2?... username: root password: root initial-size: 10 max-active: 100 min-idle: 10 max-wait: 60000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false stat-view-servlet: enabled: true url-pattern: /druid/* #login-username: admin #login-password: admin filter: stat: log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true
(參數配置,可參考: https://gitee.com/wenshao/druid/tree/master/druid-spring-boot-starter)spring
三, 編寫配置文件:sql
1, 定義數據源名稱常量 : 數據庫
package com.gy.fast.common.config.data; /** * 數據源名稱 * @author geYang * @date 2018-05-14 */ public interface DataSourceNames { String ONE = "ONE"; String TWO = "TWO"; }
2, 建立動態數據源:apache
package com.gy.fast.common.config.data; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 動態數據源 * @author geYang * @date 2018-05-14 */ public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); /** * 配置DataSource, defaultTargetDataSource爲主數據庫 */ public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } }
3, 動態數據源配置:ide
package com.gy.fast.common.config.data; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 配置多數據源 * @author geYang * @date 2018-05-14 */ @Configuration public class DynamicDataSourceConfig { /** * 建立 DataSource Bean * */ @Bean @ConfigurationProperties("spring.datasource.druid.one") public DataSource oneDataSource(){ DataSource dataSource = DruidDataSourceBuilder.create().build(); return dataSource; } @Bean @ConfigurationProperties("spring.datasource.druid.two") public DataSource twoDataSource(){ DataSource dataSource = DruidDataSourceBuilder.create().build(); return dataSource; } /** * 若是還有數據源,在這繼續添加 DataSource Bean * */ @Bean @Primary public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(2); targetDataSources.put(DataSourceNames.ONE, oneDataSource); targetDataSources.put(DataSourceNames.TWO, twoDataSource); // 還有數據源,在targetDataSources中繼續添加 System.out.println("DataSources:" + targetDataSources); return new DynamicDataSource(oneDataSource, targetDataSources); } }
4, 定義動態數據源註解:spring-boot
package com.gy.fast.common.config.data; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 多數據源註解 * @author geYang * @date 2018-05-14 */ @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default DataSourceNames.ONE; }
5, 設置數據源 AOP 代理:測試
package com.gy.fast.common.config.data; import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; /** * 數據源AOP切面處理 * @author geYang * @date 2018-05-14 */ @Aspect @Component public class DataSourceAspect implements Ordered { protected Logger logger = LoggerFactory.getLogger(getClass()); /** * 切點: 全部配置 DataSource 註解的方法 */ @Pointcut("@annotation(com.gy.fast.common.config.data.DataSource)") public void dataSourcePointCut() {} @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource ds = method.getAnnotation(DataSource.class); // 經過判斷 DataSource 中的值來判斷當前方法應用哪一個數據源 DynamicDataSource.setDataSource(ds.value()); System.out.println("當前數據源: " + ds.value()); logger.debug("set datasource is " + ds.value()); try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); logger.debug("clean datasource"); } } @Override public int getOrder() { return 1; } }
四, 修改啓動文件:
package com.gy.fast; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.Import; import com.gy.fast.common.config.data.DynamicDataSourceConfig; /** * 動態數據源配置,須要將自有的配置依賴(DynamicDataSourceConfig),將原有的依賴去除(DataSourceAutoConfiguration) * @author geYang * @date 2018-05-15 */ @Import({DynamicDataSourceConfig.class}) @SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) public class FastApplication { public static void main(String[] args) { SpringApplication.run(FastApplication.class, args); } }
五, 配置完成, 進行測試:
package com.gy.fast; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.gy.fast.common.config.data.DataSource; import com.gy.fast.common.config.data.DataSourceNames; import com.gy.fast.module.sys.entity.SysUser; import com.gy.fast.module.sys.service.SysUserService; /** * 測試多數據源 * @author geYang * @date 2018-05-15 */ @Service public class DataSourceTestService { @Autowired private SysUserService sysUserService; public SysUser test1(Long userId){ return sysUserService.selectById(userId); } @DataSource(DataSourceNames.TWO) public SysUser test2(Long userId){ return sysUserService.selectById(userId); } }
package com.gy.fast; import org.apache.commons.lang3.builder.ToStringBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.gy.fast.module.sys.entity.SysUser; @RunWith(SpringRunner.class) @SpringBootTest public class DynamicDataSourceTest { @Autowired private DataSourceTestService dataSourceTestService; @Test public void test(){ // 數據源ONE SysUser user1 = dataSourceTestService.test1(1L); System.out.println(ToStringBuilder.reflectionToString(user1)); // 數據源TWO SysUser user2 = dataSourceTestService.test2(1L); System.out.println(ToStringBuilder.reflectionToString(user2)); // 數據源ONE SysUser user3 = dataSourceTestService.test1(1L); System.out.println(ToStringBuilder.reflectionToString(user3)); } }