SpringBoot配置多資料來源(druid)
阿新 • • 發佈:2018-12-13
分析
spring本身是支援多資料來源動態切換的,AbstractRoutingDataSource這個抽象類就是spring提供的一個數據源路由的一個入口,該抽象類暴露了一個determineCurrentLookupKey()的方法,該方法返回值是Object,該返回值作為key去取Map中的DataSource。
- AbstractRoutingDataSource
- getConnection()
- determineTargetDataSource() 從Map中通過key獲取DataSource
- determineCurrentLookupKey() 獲取key
- determineCurrentLookupKey() 獲取key
- determineTargetDataSource() 從Map中通過key獲取DataSource
- getConnection()
1.建立一個執行緒執行緒安全的Holder來切換Key
public class DynamicDataSourceHolder {
public static ThreadLocal<DataSourceKey> keyThreadLocal = new ThreadLocal<>();
public static void clear(){
keyThreadLocal.remove();
}
public static void set(DataSourceKey key){
keyThreadLocal.set (key);
}
public static DataSourceKey get(){
DataSourceKey key = keyThreadLocal.get();
return null==key?DataSourceKey.DB:key;
}
}
2.繼承AbstractRoutingDataSource設定key
public class DynamicRoutingDataSource extends AbstractRoutingDataSource{
@Nullable
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.get();
}
}
3.編寫Config來初始化DataSource
@Configuration
public class DynamicDatasourceConfig {
@Autowired
ApplicationContext applicationContext;
@Bean("druid_db")//必須加上該註解,否則 @ConfigurationProperties無效
@ConfigurationProperties(prefix = "dynamic-datasource.druid-datasources.db")
public DataSource db(StandardEnvironment env){
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return common(env,druidDataSource);
}
@Bean("druid_db1")
@ConfigurationProperties(prefix = "dynamic-datasource.druid-datasources.db1")
public DataSource db1(StandardEnvironment env){
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return common(env,druidDataSource);
}
@Bean("druid_db2")
@ConfigurationProperties(prefix = "dynamic-datasource.druid-datasources.db2")
public DataSource db2(StandardEnvironment env){
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return common(env,druidDataSource);
}
@Bean("dataSource")
public DataSource dynamicDataSource(StandardEnvironment env) {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object,Object> map = new HashMap<>();
map.put(DataSourceKey.DB,applicationContext.getBean("druid_db"));
map.put(DataSourceKey.DB1,applicationContext.getBean("druid_db1"));
map.put(DataSourceKey.DB2,applicationContext.getBean("druid_db2"));
dynamicRoutingDataSource.setDefaultTargetDataSource(applicationContext.getBean("druid_db"));
dynamicRoutingDataSource.setTargetDataSources(map);
return dynamicRoutingDataSource;
}
public DataSource common(StandardEnvironment env, DruidDataSource druidDataSource){
Properties properties = new Properties();
PropertySource<?> appProperties = env.getPropertySources().get("applicationConfig: [classpath:/application.yml]");
Map<String,Object> source = (Map<String, Object>) appProperties.getSource();
properties.putAll(source);
druidDataSource.configFromPropety(properties);
return druidDataSource;
}
}
4.通過aop動態去切換key
該註解確定的該方法使用哪一個資料來源
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
DataSourceKey value() default DataSourceKey.DB;
}
資料來源的key
public enum DataSourceKey {
DB,DB1,DB2,DB3,DB4;
}
一個是攔截特定的方法和攔截有TargetDataSource註解的方法去動態切換
@Aspect
@Component
@Order(-100)//提高優先順序
public class DynamicDataSourceAop {
@Pointcut(value = "execution(public * com.yzz.boot..*.mapper ..*.*(..))")
public void defaultDataSource(){}
@Before(value = "defaultDataSource()")
public void setDefaultDataSource(){
}
//沒有註解,就選擇預設的資料來源
@Before(value = "@annotation(dataSource)&&defaultDataSource()")
public void setDynamicDataSource(TargetDataSource dataSource){
if (null == dataSource){
System.err.println("設定預設資料來源"+DataSourceKey.DB);
DynamicDataSourceHolder.set(DataSourceKey.DB);
}else {
System.err.println("切換資料來源"+dataSource.value());
DynamicDataSourceHolder.set(dataSource.value());
}
}
//清除該執行緒當前的資料
@After(value = "defaultDataSource()&&@annotation(com.yzz.boot.dyConfig.ann.TargetDataSource)")
public void clean(){
System.err.println("清除當前執行緒的資料來源");
DynamicDataSourceHolder.clear();
}
}
5.Mapper通過方法上通過註解去動態選擇資料來源
@Mapper
public interface TestMapper {
@TargetDataSource(value = DataSourceKey.DB1)
@Select("select * from t_es_test limit 5")
List<HashMap> getAll();
@TargetDataSource(value = DataSourceKey.DB2)
@Select("select * from t_es_test limit 5")
List<HashMap> getAll1();
}
6.程式入口去除預設的連線池的配置類
//去除預設的連線池
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//掃描響應的包
@MapperScan("com.yzz.boot.*.mapper")
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class);
}
}
7.配置檔案
spring:
profiles:
active: system
dynamic-datasource:
druid:
filters: stat
maxActive: 20
initialSize: 1
maxWait: 30000
minIdle: 10
maxIdle: 15
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
maxOpenPreparedStatements: 20
removeAbandoned: true
removeAbandonedTimeout: 1800
logAbandoned: true
druid-datasources:
db:
url: jdbc:mysql://192.168.1.12:3306/yzz
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
db1:
url: jdbc:mysql://192.168.1.12:3306/yzz
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
db2:
url: jdbc:mysql://192.168.1.12:3306/yzz
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jdbc-conn:
url: jdbc:mysql://192.168.1.12:3306/yzz
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
總結
通過註解來動態原則key,spring的DataSource路由選擇器通過key從之前設定進去的DataSource Map中獲取響應的DataSource,從而達到了動態切換的目的。通過ThreadLocal來儲存key,保證了數=資料在多執行緒環境下的正確性。