springboot項目自定義注解實現(xiàn)的多數(shù)據(jù)源切換

一、主要依賴

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.4.RELEASE</version><relativePath/></parent><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.1.4.RELEASE</version></dependency>dependency>

? ? <groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency>

二、yml

# 數(shù)據(jù)源配置

spring:

? ? datasource:

? ? ? ? type: com.alibaba.druid.pool.DruidDataSource

? ? ? ? driverClassName: com.mysql.jdbc.Driver

? ? ? ? druid:

? ? ? ? ? ? # 主庫數(shù)據(jù)源

? ? ? ? ? ? master:

? ? ? ? ? ? ? ? url: jdbc:mysql://127.0.0.1:3306/master?characterEncoding=UTF-8? ? ? ? ? ? ? ? username: root

? ? ? ? ? ? ? ? password: root

? ? ? ? ? ? #樹熊數(shù)據(jù)源

? ? ? ? ? ? slave:

? ? ? ? ? ? ? ? enabled : true? ? ? ? ? ? ? ? url: jdbc:mysql:////127.0.0.1:3306/slave?characterEncoding=UTF-8? ? ? ? ? ? ? ? username: root

? ? ? ? ? ? ? ? password: root


? ? ? ? ? ? # 初始連接數(shù)

? ? ? ? ? ? initial-size: 10? ? ? ? ? ? # 最大連接池數(shù)量

? ? ? ? ? ? max-active: 100? ? ? ? ? ? # 最小連接池數(shù)量

? ? ? ? ? ? min-idle: 10? ? ? ? ? ? # 配置獲取連接等待超時的時間

? ? ? ? ? ? max-wait: 60000? ? ? ? ? ? # 打開PSCache,并且指定每個連接上PSCache的大小

? ? ? ? ? ? pool-prepared-statements:true? ? ? ? ? ? max-pool-prepared-statement-per-connection-size: 20? ? ? ? ? ? # 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒

? ? ? ? ? ? timeBetweenEvictionRunsMillis: 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/*? ? ? ? ? ? filter:

? ? ? ? ? ? ? ? stat:

? ? ? ? ? ? ? ? ? ? log-slow-sql: true

? ? ? ? ? ? ? ? ? ? slow-sql-millis: 1000

? ? ? ? ? ? ? ? ? ? merge-sql: false

? ? ? ? ? ? ? ? wall:

? ? ? ? ? ? ? ? ? ? config:

? ? ? ? ? ? ? ? ? ? ? ? multi-statement-allow: true

三、實現(xiàn)

3.1、@DataSource和DataSourceType

/** * 數(shù)據(jù)源

* @author DUCHONG

*/publicenum DataSourceType

{

? ? /**? ? * 主庫

? ? */? ? MASTER,

? ? /**? ? * 從庫

? ? */? ? SLAVE

}

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 自定義多數(shù)據(jù)源切換注解

*

* @author DUCHONG

*/@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)public@interface DataSource

{

? ? /**? ? * 切換數(shù)據(jù)源名稱

? ? */publicDataSourceType value()default DataSourceType.MASTER;

}

3.2、DynamicDataSourceContextHolder

import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 數(shù)據(jù)源切換處理

*

* @author DUCHONG

*/publicclass DynamicDataSourceContextHolder

{

? ? publicstaticfinalLogger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

? ? /**? ? * 使用ThreadLocal維護(hù)變量,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,

? ? *? 所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。

? ? */privatestaticfinalThreadLocal CONTEXT_HOLDER =newThreadLocal<>();

? ? /**? ? * 設(shè)置數(shù)據(jù)源的變量

? ? */publicstaticvoid setDateSourceType(String dsType)

? ? {

? ? ? ? log.info("切換到{}數(shù)據(jù)源", dsType);

? ? ? ? CONTEXT_HOLDER.set(dsType);

? ? }

? ? /**? ? * 獲得數(shù)據(jù)源的變量

? ? */publicstatic String getDateSourceType()

? ? {

? ? ? ? return CONTEXT_HOLDER.get();

? ? }

? ? /**? ? * 清空數(shù)據(jù)源變量

? ? */publicstaticvoid clearDateSourceType()

? ? {

? ? ? ? CONTEXT_HOLDER.remove();

? ? }

}

3.3、繼承AbstractRoutingDataSource

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;import java.util.Map;/** * 動態(tài)數(shù)據(jù)源

*

* @author DUCHONG

*/publicclassDynamicDataSourceextends AbstractRoutingDataSource

{

? ? publicDynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources)

? ? {

? ? ? ? super.setDefaultTargetDataSource(defaultTargetDataSource);

? ? ? ? super.setTargetDataSources(targetDataSources);

? ? ? ? super.afterPropertiesSet();

? ? }

? ? @Override

? ? protected Object determineCurrentLookupKey()

? ? {

? ? ? ? return DynamicDataSourceContextHolder.getDateSourceType();

? ? }

}

3.4、定義切面

import com.starfast.admin.common.annotation.DataSource;import com.starfast.admin.datasource.DynamicDataSourceContextHolder;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.annotation.Order;import org.springframework.stereotype.Component;import java.lang.reflect.Method;/** * 多數(shù)據(jù)源處理

* @author DUCHONG

*/@Aspect

@Order(1)

@Componentpublicclass DataSourceAspect

{

? ? protectedLogger logger = LoggerFactory.getLogger(getClass());

? ? @Pointcut("@annotation(com.duchong.common.annotation.DataSource)")

? ? publicvoid dsPointCut()

? ? {

? ? }

? ? @Around("dsPointCut()")

? ? publicObject around(ProceedingJoinPoint point)throws Throwable

? ? {

? ? ? ? MethodSignature signature = (MethodSignature) point.getSignature();

? ? ? ? Method method = signature.getMethod();

? ? ? ? DataSource dataSource = method.getAnnotation(DataSource.class);

? ? ? ? if(null!=dataSource)

? ? ? ? {

? ? ? ? ? ? DynamicDataSourceContextHolder.setDateSourceType(dataSource.value().name());

? ? ? ? }

? ? ? ? try? ? ? ? {

? ? ? ? ? ? return point.proceed();

? ? ? ? }

? ? ? ? finally? ? ? ? {

? ? ? ? ? ? // 銷毀數(shù)據(jù)源 在執(zhí)行方法之后? ? ? ? ? ? DynamicDataSourceContextHolder.clearDateSourceType();

? ? ? ? }

? ? }

}

3.5、@Configuration

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;import com.starfast.admin.common.enums.DataSourceType;import com.starfast.admin.datasource.DynamicDataSource;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;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;/** * druid 配置多數(shù)據(jù)源

*

* @author DUCHONG

*/@Configurationpublicclass DruidConfig

{

? ? @Bean

? ? @ConfigurationProperties("spring.datasource.druid.master")

? ? public DataSource masterDataSource()

? ? {

? ? ? ? return DruidDataSourceBuilder.create().build();

? ? }

? ? @Bean

? ? @ConfigurationProperties("spring.datasource.druid.slave")

? ? @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")

? ? public DataSource slaveDataSource()

? ? {

? ? ? ? return DruidDataSourceBuilder.create().build();

? ? }

? ? @Bean(name = "dynamicDataSource")

? ? @Primary

? ? public DynamicDataSource dataSource()

? ? {

? ? ? ? Map targetDataSources =newHashMap<>();

? ? ? ? targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource());

? ? ? ? targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource());

? ? ? ? returnnew DynamicDataSource(masterDataSource(), targetDataSources);

? ? }

}

3.6、使用

需要切換數(shù)據(jù)源的方法上加

@DataSource(value = DataSourceType.SLAVE)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容