springboot项目自定义注解实现的多数据源切换
一、主要依赖
二、yml
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://127.0.0.1:3306/master?characterEncoding=UTF-8username: root
password: root
#树熊数据源
slave:
enabled : trueurl: jdbc:mysql:////127.0.0.1:3306/slave?characterEncoding=UTF-8username: root
password: root
# 初始连接数
initial-size: 10# 最大连接池数量
max-active: 100# 最小连接池数量
min-idle: 10# 配置获取连接等待超时的时间
max-wait: 60000# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements:truemax-pool-prepared-statement-per-connection-size: 20# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000validation-query: SELECT 1 FROM DUAL
test-while-idle:truetest-on-borrow:falsetest-on-return:falsestat-view-servlet:
enabled: trueurl-pattern: /druid/*filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true
三、实现
3.1、@DataSource和DataSourceType
/** * 数据源
* @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;
/** * 自定义多数据源切换注解
*
* @author DUCHONG
*/@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)public@interface DataSource
{
/*** 切换数据源名称
*/publicDataSourceType value()default DataSourceType.MASTER;
}
3.2、DynamicDataSourceContextHolder
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** * 数据源切换处理
*
* @author DUCHONG
*/publicclass DynamicDataSourceContextHolder
{
publicstaticfinalLogger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/*** 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
*所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/privatestaticfinalThreadLocal CONTEXT_HOLDER =newThreadLocal<>();
/*** 设置数据源的变量
*/publicstaticvoid setDateSourceType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/*** 获得数据源的变量
*/publicstatic String getDateSourceType()
{
return CONTEXT_HOLDER.get();
}
/*** 清空数据源变量
*/publicstaticvoid clearDateSourceType()
{
CONTEXT_HOLDER.remove();
}
}
3.3、继承AbstractRoutingDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/** * 动态数据源
*
* @author DUCHONG
*/publicclassDynamicDataSourceextends AbstractRoutingDataSource
{
publicDynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources)
{
【springboot项目自定义注解实现的多数据源切换】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;
/** * 多数据源处理
* @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{
// 销毁数据源 在执行方法之后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 配置多数据源
*
* @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 = "https://www.it610.com/article/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、使用
需要切换数据源的方法上加
@DataSource(value = https://www.it610.com/article/DataSourceType.SLAVE)
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- python自定义封装带颜色的logging模块
- 解决SpringBoot引用别的模块无法注入的问题
- 列出所有自定义的function和view
- 17|17 关山松 第二课作业#公众号项目# D20
- RxJava|RxJava 在Android项目中的使用(一)
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- 靠QQ月入上万灰色暴利偏门的项目
- spring|spring boot项目启动websocket