Springboot+aop實(shí)現(xiàn)配置多數(shù)據(jù)源的示例代碼
前言
? 這是我司在對(duì)于動(dòng)態(tài)數(shù)據(jù)源的一個(gè)處理方法,采用動(dòng)態(tài)加載數(shù)據(jù)庫(kù)信息實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源的切換。通過使用Spring AOP + 注解來替換當(dāng)前線程ThreadLocal中的值,并且通過重寫AbstractRoutingDataSource類重寫determineCurrentLookUpKey()方法,實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換,滿足功能實(shí)現(xiàn)的代碼0侵入性,并且高度解耦,實(shí)現(xiàn)可拔插功能效果
1、添加依賴(本項(xiàng)目是一個(gè)springboot項(xiàng)目)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
<scope>runtime</scope>
</dependency>
2、定義數(shù)據(jù)庫(kù)類型枚舉類(enum)
/**
* 數(shù)據(jù)庫(kù)配置名字字段
* @author zh
* @data 17點(diǎn)14分
*
*/
public enum DBTypeEnum {
cs1db("cs1-db"), cs2db("cs2-db");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
此枚舉主要是用于簡(jiǎn)化if-else語句。
3、定義注解(annotation)
/**
* @author zh
* @data 17點(diǎn)18分
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSourceSwitch {
/****
* 順便設(shè)置下默認(rèn)數(shù)據(jù)源
* @return
*/
DBTypeEnum value() default DBTypeEnum.cs1db;
}
定義DataSourceSwitch注解,默認(rèn)數(shù)據(jù)源為cs1db(數(shù)據(jù)庫(kù)一)。
4、編寫ThreadLocal存儲(chǔ)數(shù)據(jù)上下文信息
/**
* @author zh
*/
public class DbContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
/**
* 設(shè)置數(shù)據(jù)源
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {
contextHolder.set(dbTypeEnum.getValue());
}
/**
* 取得當(dāng)前數(shù)據(jù)源
* @return
*/
public static String getDbType() {
return (String) contextHolder.get();
}
/**
* 清除上下文數(shù)據(jù)
*/
public static void clearDbType() {
contextHolder.remove();
}
}
工具類DbContextHolder用于存儲(chǔ)數(shù)據(jù)名上下文。
5、獲取數(shù)據(jù)源信息
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author zh
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 取得當(dāng)前使用哪個(gè)數(shù)據(jù)源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
工具類DynamicDataSource繼承AbstractRoutingDataSource重寫determineCurrentLookupKey()方法,從工具類DbContextHolder中獲取當(dāng)前數(shù)據(jù)源信息。
6、自定義切面(Aspect)
/**
* @author zh
* 數(shù)據(jù)源AOP注解實(shí)現(xiàn)
*/
@Component
@Aspect
@Order(-100)
public class DataSourceSwitchAspect {
private Logger log= LoggerFactory.getLogger(DataSourceSwitchAspect.class);
@Pointcut("execution(* com.zh.cn.business.cs1db..*.*(..))")
private void cs1dbAspect() {
}
@Pointcut("execution(* com.zh.cn.business.cs2db..*.*(..))")
private void cs2dbAspect() {
}
@Before( "cs1dbAspect()" )
public void basic(JoinPoint joinPoint) {
//log.info("切換到cs1db 數(shù)據(jù)源...");
setDataSource(joinPoint, DBTypeEnum.cs1db);
}
@Before("cs2dbAspect()" )
public void order (JoinPoint joinPoint) {
//log.info("切換到cs2db 數(shù)據(jù)源...");
setDataSource(joinPoint,DBTypeEnum.cs2db);
}
/**
* 添加注解方式,如果有注解優(yōu)先注解,沒有則按傳過來的數(shù)據(jù)源配置
* @param joinPoint
* @param dbTypeEnum
*/
private void setDataSource(JoinPoint joinPoint, DBTypeEnum dbTypeEnum) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
DataSourceSwitch dataSourceSwitch = methodSignature.getMethod().getAnnotation(DataSourceSwitch.class);
if (Objects.isNull(dataSourceSwitch) || Objects.isNull(dataSourceSwitch.value())) {
DbContextHolder.setDbType(dbTypeEnum);
}else{
// log.info("根據(jù)注解來切換數(shù)據(jù)源,注解值為:"+dataSourceSwitch.value());
switch (dataSourceSwitch.value().getValue()) {
case "cs1-db":
DbContextHolder.setDbType(DBTypeEnum.cs1db);
break;
case "cs2-db":
DbContextHolder.setDbType(DBTypeEnum.cs2db);
break;
default:
DbContextHolder.setDbType(dbTypeEnum);
}
}
}
}
通過springAop自定義切面,切入點(diǎn)在* com.zh.cn.business.cs2db..*.*(..)),表示切入點(diǎn)在com.zh.cn.business.cs2db包下的任意子包、任意方法和任意返回值,并通過setDataSource()方法設(shè)置工具類DbContextHolder中數(shù)據(jù)源信息。
7、配置數(shù)據(jù)源信息
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.zh.cn.Utils.DynamicDataSource;
import com.zh.cn.constants.DBTypeEnum;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.annotation.MapperScan;
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.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 加載DataSource
*/
@Configuration
@MapperScan({"com.zh.cn.**.mapper"})
public class MybatisPlusConfig {
/**
* mapper-plus分頁(yè)插件<br>
* 文檔:http://mp.baomidou.com<br>
*/
/* @Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
//paginationInterceptor.setLocalPage(true);// 開啟 PageHelper 的支持
return paginationInterceptor;
}*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//樂觀鎖
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
@Bean(name = "cs1db")
@ConfigurationProperties(prefix = "spring.datasource.druid.cs1-db" )
public DataSource cs1db () {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "cs2db")
@ConfigurationProperties(prefix = "spring.datasource.druid.cs2-db" )
public DataSource cs2db () {
return DruidDataSourceBuilder.create().build();
}
/**
* 動(dòng)態(tài)數(shù)據(jù)源配置
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource (@Qualifier("cs1db") DataSource cs1db,
@Qualifier("cs2db") DataSource cs2db){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map< Object, Object > targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.cs1db.getValue(), cs1db );
targetDataSources.put(DBTypeEnum.cs2db.getValue(), cs2db);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(cs1db);//指定默認(rèn)
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(cs1db(),cs2db()));
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*/*Mapper.xml"));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setPlugins(new Interceptor[]{
mybatisPlusInterceptor()
});
return sqlSessionFactory.getObject();
}
}
通過覆蓋默認(rèn)的DataSource進(jìn)行加載動(dòng)態(tài)數(shù)據(jù)源的配置信息,通過**multipleDataSource()方法設(shè)置數(shù)據(jù)源,最后通過sqlSessionFactory()**設(shè)置數(shù)據(jù)源的最終加載
8、編寫配置文件
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
cs1-db:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/short-chain?useUnicode=true&characterEncoding=UTF-8
username: root
password:
initialSize: 10
minIdle: 20
maxActive: 100
maxWait: 60000
cs2-db:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/short-chain?useUnicode=true&characterEncoding=UTF-8
username: root
password:
initialSize: 10
minIdle: 20
maxActive: 100
maxWait: 60000
maxActive: 100
maxWait: 60000到此這篇關(guān)于Springboot+aop實(shí)現(xiàn)配置多數(shù)據(jù)源的示例代碼的文章就介紹到這了,更多相關(guān)Springboot aop多數(shù)據(jù)源內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用POI實(shí)現(xiàn)excel文件的導(dǎo)入和導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了Java如何使用POI實(shí)現(xiàn)excel文件的導(dǎo)入和導(dǎo)出功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
Mybatis日期格式自動(dòng)轉(zhuǎn)換需要用到的兩個(gè)注解說明
這篇文章主要介紹了Mybatis日期格式自動(dòng)轉(zhuǎn)換需要用到的兩個(gè)注解說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
SpringBoot+Vue添加騰訊云人臉識(shí)別的項(xiàng)目實(shí)踐
人臉識(shí)別是一種基于人臉特征進(jìn)行身份認(rèn)證和識(shí)別的技術(shù),本文主要介紹了SpringBoot+Vue添加騰訊云人臉識(shí)別的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
Spring Boot2解決idea console 控制臺(tái)輸出亂碼的問題
這篇文章主要介紹了Spring Boot2解決idea console 控制臺(tái)輸出亂碼的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java數(shù)據(jù)結(jié)構(gòu) 遞歸之迷宮回溯案例講解
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)遞歸之迷宮回溯案例講解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
SpringBoot實(shí)現(xiàn)登錄攔截的示例代碼
如果我們不進(jìn)行登錄攔截的話,即使我們跳過登錄頁(yè)面直接去訪問任意一個(gè)頁(yè)面也能訪問成功,那么登錄功能就沒有意義,同時(shí)也會(huì)存在安全問題,本文就來介紹一下SpringBoot登錄攔截,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
java代理 jdk動(dòng)態(tài)代理應(yīng)用案列
java代理有jdk動(dòng)態(tài)代理、cglib代理,這里只說下jdk動(dòng)態(tài)代理,jdk動(dòng)態(tài)代理主要使用的是java反射機(jī)制,需要了解的朋友可以參考下2012-11-11

