springboot 配置多數(shù)據(jù)源(Aop+注解實現(xiàn))
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
在實際項目中很多時候會涉及到多個數(shù)據(jù)庫的訪問,或者數(shù)據(jù)庫讀寫分離的形式。
下面通過使用 Aspect+注解來實現(xiàn)mybatis的多數(shù)據(jù)源配置? ?
動態(tài)數(shù)據(jù)源流程說明
Spring Boot 的動態(tài)數(shù)據(jù)源,本質(zhì)上是把多個數(shù)據(jù)源存儲在一個 Map 中,當(dāng)需要使用某個數(shù)據(jù)源時,從 Map 中獲取此數(shù)據(jù)源進行處理。而在 Spring 中,已提供了抽象類?AbstractRoutingDataSource?來實現(xiàn)此功能。因此,我們在實現(xiàn)動態(tài)數(shù)據(jù)源的,只需要繼承它,實現(xiàn)自己的獲取數(shù)據(jù)源邏輯即可。動態(tài)數(shù)據(jù)源流程如下所示:

用戶訪問應(yīng)用,在需要訪問不同的數(shù)據(jù)源時,根據(jù)自己的數(shù)據(jù)源路由邏輯,訪問不同的數(shù)據(jù)源,實現(xiàn)對應(yīng)數(shù)據(jù)源的操作。本示例中的兩數(shù)據(jù)庫的分別有一個表?t_user,表結(jié)構(gòu)一致,為便于說明,兩個表中的數(shù)據(jù)是不一樣的。
實現(xiàn)動態(tài)數(shù)據(jù)源
1.數(shù)據(jù)庫連接信息配置
在application.yml中添加如下信息,可根據(jù)自己的項目進行替換相應(yīng)的prefix和連接信息。
springboot:
??datasource:
????master:
??????driver-class-name:?com.mysql.cj.jdbc.Driver
??????jdbc-url:?jdbc:mysql://localhost:3306/spring?useUnicode=true&characherEncoding=utf-8&useSSL=true&serverTimezone=UTC
??????username:?root
??????password:?123456
????slave:
??????driver-class-name:?com.mysql.cj.jdbc.Driver
??????jdbc-url:?jdbc:mysql://localhost:3306/spring1?useUnicode=true&characherEncoding=utf-8&useSSL=true&serverTimezone=UTC
??????username:?root
??????password:?123456
2.數(shù)據(jù)源配置
根據(jù)連接信息,把數(shù)據(jù)源注入到 Spring 中,添加?DynamicDataSourceConfig?文件,配置如下:
@Configuration
public?class?DynamicDataSourceConfig?{
????@Bean(DataSourceConstants.DS_KEY_MASTER)
????@ConfigurationProperties(prefix?=?"springboot.datasource.master")
????public?DataSource?masterDataSource()?{
????????return?DataSourceBuilder.create().build();
????}
????@Bean(DataSourceConstants.DS_KEY_SLAVE)
????@ConfigurationProperties(prefix?=?"springboot.datasource.slave")
????public?DataSource?slaveDataSource()?{
????????return?DataSourceBuilder.create().build();
????}
}
DataSourceConstants類:
public?class?DataSourceConstants?{
????public?final?static?String?DS_KEY_MASTER?=?"master";
????public?final?static?String?DS_KEY_SLAVE?=?"slave";
}
分別將master和slave數(shù)據(jù)源注入到spring容器中。
3.動態(tài)數(shù)據(jù)源設(shè)置
添加動態(tài)數(shù)據(jù)源類
DynamicDataSource?和?DynamicDataSourceContextHolder類
import?org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public?class?DynamicDataSource?extends?AbstractRoutingDataSource?{
????@Override
????protected?Object?determineCurrentLookupKey()?{
????????return?DynamicDataSourceContextHolder.getContextKey();
????}
}
public?class?DynamicDataSourceContextHolder?{
????/*?動態(tài)數(shù)據(jù)源名稱上下文*/
????private?static?final?ThreadLocal?DATASOURCE_CONTEXT_KEY_HOLDER?=?new?ThreadLocal<>();
????/*?設(shè)置/切換數(shù)據(jù)源*/
????public?static?void?setContextKey(String?key)?{
????????System.out.println("切換數(shù)據(jù)源"+key);
????????DATASOURCE_CONTEXT_KEY_HOLDER.set(key);
????}
????/*?獲取數(shù)據(jù)源名稱?*/
????public?static?String?getContextKey()?{
????????String?key?=?DATASOURCE_CONTEXT_KEY_HOLDER.get();
????????return?key?==?null???DataSourceConstants.DS_KEY_MASTER?:?key;
????}
????/*刪除當(dāng)前數(shù)據(jù)源名稱*/
????public?static?void?removeContextKey()?{
????????DATASOURCE_CONTEXT_KEY_HOLDER.remove();
????}
}
3.設(shè)置動態(tài)數(shù)據(jù)源為主數(shù)據(jù)源
在前面的數(shù)據(jù)源配置文件?DynamicDataSourceConfig?中,添加以下代碼:
@Bean
@Primary
public?DataSource?dynamicDataSource()?{
????Map使用注解?
Primary?優(yōu)先從動態(tài)數(shù)據(jù)源中獲取同時,需要在?
DynamicDataSourceConfig?中,排除?DataSourceAutoConfiguration?的自動配置,否則 會出現(xiàn)The dependencies of some of the beans in the application context form a cycle的錯誤。在·?DynamicDataSourceConfig類上添加@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })1
DynamicDataSourceConfig?最終代碼如下:
@Configuration
@EnableAutoConfiguration(exclude?=?{?DataSourceAutoConfiguration.class?})
public?class?DynamicDataSourceConfig?{
????@Bean(DataSourceConstants.DS_KEY_MASTER)
????@ConfigurationProperties(prefix?=?"springboot.datasource.master")
????public?DataSource?masterDataSource()?{
????????return?DataSourceBuilder.create().build();
????}
????@Bean(DataSourceConstants.DS_KEY_SLAVE)
????@ConfigurationProperties(prefix?=?"springboot.datasource.slave")
????public?DataSource?slaveDataSource()?{
????????return?DataSourceBuilder.create().build();
????}
????@Bean
????@Primary
????public?DataSource?dynamicDataSource()?{
????????Map使用AOP選擇數(shù)據(jù)源
1.自定義一個annotation
DbAnnotation注解,用于mapper接口上:
@Target({?ElementType.METHOD,?ElementType.TYPE?})
@Retention(RetentionPolicy.RUNTIME)
public?@interface?DbAnnotation?{
????/**
?????*?數(shù)據(jù)源名稱,默認master
?????*/
????String?value()?default?DataSourceConstants.DS_KEY_MASTER;
}
2.定義數(shù)據(jù)源切面
定義數(shù)據(jù)源切面,用于攔截添加了@DbAnnotation注解的類或方法:
添加maven依賴
????
????org.springframework.boot ????
????spring-boot-starter-aop ?
定義切面DynamicDataSourceAspect類:
@Aspect
@Component
public?class?DynamicDataSourceAspect?{
????//攔截DbAnnotation
????@Pointcut("@within(com.mj.vscodedemo.annotation.DbAnnotation)")
????public?void?dataSourcePointCut()?{
????}
????@Around("dataSourcePointCut()")
????public?Object?around(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
????????String?dsKey?=?this.getDSAnnotation(joinPoint).value();
????????DynamicDataSourceContextHolder.setContextKey(dsKey);
????????try?{
????????????return?joinPoint.proceed();
????????}?catch?(Exception?ex)?{
????????????throw?ex;
????????}?finally?{
????????????DynamicDataSourceContextHolder.removeContextKey();
????????}
????}
????/**
?????*?根據(jù)類或方法獲取數(shù)據(jù)源注解
?????*/
????private?DbAnnotation?getDSAnnotation(ProceedingJoinPoint?joinPoint)?{
????????//mybatis生成的代理類,所以獲取它的接口來獲取DbAnnotation注解信息
????????Class>?targetClass?=?joinPoint.getTarget().getClass().getInterfaces()[0];
????????DbAnnotation?dsAnnotation?=?targetClass.getAnnotation(DbAnnotation.class);
????????//?先判斷類的注解,再判斷方法注解
????????if?(Objects.nonNull(dsAnnotation))?{
????????????return?dsAnnotation;
????????}?else?{
????????????MethodSignature?methodSignature?=?(MethodSignature)?joinPoint.getSignature();
????????????DbAnnotation?annotation?=?methodSignature.getMethod().getAnnotation(DbAnnotation.class);
????????????return?annotation;
????????}
????}
}
在mapper接口上添加@DbAnnotation注解,分別為master和slave數(shù)據(jù)源添加了個測試的mapper:
@Mapper
@Repository
@DbAnnotation(DataSourceConstants.DS_KEY_MASTER)
public?interface?UserMapper?{
????Integer?getUserId();
}
@Mapper
@Repository
@DbAnnotation(DataSourceConstants.DS_KEY_SLAVE)
public?interface?UserMapper1?{
????Integer?getUserId();
}
多數(shù)據(jù)源配置已經(jīng)完成了,下面就可以進行調(diào)用測試訪問了
測試
添加一個controller并直接調(diào)用mapper的方法
@RestController
public?class?HelloController?{
????@Autowired
????private?UserMapper?userMapper;
????@Autowired
????private?UserMapper1?userMapper1;
????@ApiOperation(value?=?"sayHi")
????@GetMapping("/sayHi")
????public?String?sayHi()?{
????????int?i?=?userMapper.getUserId();
????????System.out.println("用戶1數(shù)量"?+?i);
????????int?i2?=?userMapper1.getUserId();
????????System.out.println("用戶2數(shù)量"?+?i2);
????????return?"你好啊";
????}
}
輸出如下:
切換數(shù)據(jù)源master
2020-11-29?22:44:58.933??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-1?-?Starting...
2020-11-29?22:45:00.451??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-1?-?Start?completed.
用戶1數(shù)量1
切換數(shù)據(jù)源slave
2020-11-29?22:45:00.664??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-2?-?Starting...
2020-11-29?22:45:00.728??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-2?-?Start?completed.
用戶2數(shù)量2
可以看到分別訪問的是master和slave的數(shù)據(jù)源。
下面就可以進行db讀寫分離愉快的寫代碼了。
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/mj_940620/article/details/110356785
粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領(lǐng)取
???
?長按上方微信二維碼?2 秒 即可獲取資料
感謝點贊支持下哈?
