使用springboot項目時用到了多數據源配置,在網上看了很多總感覺達不到自己的要求,所以瀏覽完之後決定自己寫一個。
廢話不說,上代碼
pom.xml需要用到的支持
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.boot</groupId>
<artifactId>proliu</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>proliu</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</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-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
我比較懶就把所有的全都粘過來了,但是這是一個我用來測試多數據源的項目,多餘的支持包不多。
再來看application.yml
#使用多數據源配置,自定義配置我訂的規則,就是在項目中讓程序讀取到的配置的key值
spring:
#list 數據源名字的集合 名字自己訂只需要在每個數據源上一級以該名字定義 第一個爲默認數據源
list: master,save
datasource:
#數據源配置基本配置 其他的統一配置可以自己加 我比較懶 注:該級別的名字應該與上邊list一一對應,不然會怎麼樣我也不知道 嘿嘿
master:
driverClassCame: com.mysql.jdbc.Driver
url: ://*************:3306/*************?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
username: root
password:
save:
driverClassCame: com.mysql.jdbc.Driver
url: jdbc:mysql://*************:3306/*************?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
username: root
password:
#使用mybatis 需要的兩個配置信息
mybatis:
mapper-locations: classpath*:com/example/boot/proliu/map/*.xml
type-aliases-package: com.example.boot.proliu.entity
使用自定義多數據源配置 首先就是再啓動類去掉項目自帶的自動加載數據源代碼
//exclude = DataSourceAutoConfiguration.class 去掉自動加載數據源 不然會造成循環依賴
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = "com.example.boot.proliu.map")//mapper掃描
public class ProliuApplication {
public static void main(String[] args) {
SpringApplication.run(ProliuApplication.class, args);
}
}
使用多數據源需要自己定義配置文件信息獲取和數據源配置 進入主題
package com.example.boot.proliu.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 繼承AbstractRoutingDataSource重寫determineCurrentLookupKey
* 自己定義需要那個數據源
*/
public class MyDynamicDataSource extends AbstractRoutingDataSource {
//線程獨有數據源切換 使用 ThreadLocal
private final static ThreadLocal<String > databasekey = new ThreadLocal<>();
/**
* 配置DataSource, defaultTargetDataSource爲主數據庫 TargetDataSources爲數據源集合
*/
public MyDynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
public static void setDatabasekey(String key) {
databasekey.set(key);
}
public static String getDatabasekey() {
return databasekey.get();
}
public static void removeDatabasekey() {
databasekey.remove();
}
@Override
protected Object determineCurrentLookupKey() {
return databasekey.get();
}
}
簡單解釋一下 AbstractRoutingDataSource, 該類充當了DataSource的路由中介, 能有在運行時, 根據某種key值來動態切換到真正的DataSource上。
有了數據源切換類了,下邊貼一下數據源加載類
package com.example.boot.proliu.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.core.env.Environment;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class MyDataSourse {
@Autowired
Environment env;
/**
* 將獲取的數據源配置註冊到MyDynamicDataSource 或者說 AbstractRoutingDataSource 中
* @return
*/
@Bean
@Primary
public MyDynamicDataSource dataSource() {
Map<Object, Object> targetDataSources = getdate();
return new MyDynamicDataSource((DataSource)targetDataSources.get("default"), targetDataSources);
}
/**
* 加載數據源時需要讀取數據配配置到DruidDataSource類裏 我這裏用阿里的驅動
* 默認數據源我這使用default做爲key
* 這裏用到了Environment來讀取配置
* 我和看的資料裏就這有分歧 我認爲這樣更加簡單清晰
* @return
*/
private Map<Object,Object> getdate(){
List<String> listTest = env.getProperty("spring.list", List.class);
Map<Object,Object> dataSourceMap = new HashMap<>();
for (String s : listTest) {
DruidDataSource oneDateSource = new DruidDataSource();
oneDateSource.setDriverClassName(env.getProperty("spring.datasource."+s+".driverClassCame"));
oneDateSource.setUrl(env.getProperty("spring.datasource."+s+".url"));
oneDateSource.setUsername(env.getProperty("spring.datasource."+s+".username"));
oneDateSource.setPassword(env.getProperty("spring.datasource."+s+".password"));
dataSourceMap.put(s,oneDateSource);
}
dataSourceMap.put("default",dataSourceMap.get(listTest.get(0)));
return dataSourceMap;
}
}
到這基本的數據源配置就完成了。只需要在需切換的數據源的地方
MyDynamicDataSource.setDatabasekey(key); //參數爲配置中list中的數據源名稱
注意 使用這種方式調用完後應該釋放數據源回覆到默認狀態
MyDynamicDataSource.removeDatabasekey();
但是這麼搞對代碼侵入性太強 因此使用aop形式對服務方法進行增強,在增強中進行數據源切換
首先 定義註解 上代碼
package com.example.boot.proliu.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicDateSource {
String value() default "";
}
這裏不多解釋,不懂的可以看看java註解這一塊
接下來 就是增強類 代碼
package com.example.boot.proliu.aspect;
import com.example.boot.proliu.config.MyDynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 裏邊用到的幾個註解
*
* @Aspect
* @Pointcut
* @Before
* @After 這都是aop這一塊的註解
*/
@Aspect
@Component
public class DataSpurceAspect {
//定義切面
@Pointcut("@annotation(com.example.boot.proliu.aspect.DynamicDateSource)")
public void annotationPointCut() {
}
//前置增強 檢查方法是否需要切換數據源 和數據源切換
@Before("annotationPointCut()")
public void before(JoinPoint joinPoint){
MethodSignature sign = (MethodSignature)joinPoint.getSignature();
Method method = sign.getMethod();
DynamicDateSource annotation = method.getAnnotation(DynamicDateSource.class);
if(annotation != null){
MyDynamicDataSource.setDatabasekey(annotation.value());
}
}
//後置增強 方法調用完之後回覆默認數據源 @After註解是不管怎樣都會執行
@After("annotationPointCut()")
public void after(){
if(null != MyDynamicDataSource.getDatabasekey())
MyDynamicDataSource.removeDatabasekey();
}
}
到配置基本完成
只需要在需要的切換的方法上加上
@DynamicDateSource(“數據源的KEY”)註解就可以了
附上項目地址
springboot 動態數據源配置
項目地址 https://github.com/ljhaa/dynamicDataSource.git