目录结构:
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* @Description 动态数据源AOP切换
* @Author WangKun
* @Date 2023/4/4 11:23
* @Version
*/
@Aspect
@Component
@Order(1) //配置加载顺序
public class DataSourceAspect {
@Pointcut("@annotation(DB类的路径)")
public void doPointCut() {
}
/**
* @param point
* @Description 切点
* @Throws
* @Return java.lang.Object
* @Date 2023-04-04 11:24:11
* @Author WangKun
*/
@Around("doPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
DB dataSource = getDataSource(point);
if (!Objects.isNull(dataSource)) {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
}
try {
return point.proceed();
} finally {
// 在执行方法之后 销毁数据源
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
/**
* @param point
* @Description 获取@DB注解
* @Throws
* @Return com.harmonywisdom.common.system.annotation.DB
* @Date 2023-04-04 11:25:03
* @Author WangKun
*/
public DB getDataSource(ProceedingJoinPoint point) {
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
// 判断是否存在@DateBase注解
if (className.isAnnotationPresent(DB.class)) {
//获取注解
return className.getAnnotation(DB.class);
}
Method method = ((MethodSignature) point.getSignature()).getMethod();
return method.getAnnotation(DB.class);
}
}
import java.lang.annotation.*;
/**
* @Description 注解方式切换 在**ServiceImpl中的方法上面使用此注解,默认位主数据库可不写, @DB(DataSourceType.DB2)
* @Author WangKun
* @Date 2023/4/4 14:23
* @Version
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DB
{
/**
* 切换数据源名称
*/
DataSourceType value() default DataSourceType.DB1;
}
\
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @Description Hikari多数据源配置
* @Author WangKun
* @Date 2023/4/4 15:00
* @Version
*/
@Configuration
public class HikariConfig {
@Bean(name = "dataSourceDb1")
@ConfigurationProperties("spring.datasource.db1")
public DataSource dataSourceDb1(HikariProperties properties) {
HikariDataSource dataSource = DataSourceBuilder.create().type(HikariDataSource.class).build();
return properties.dataSource(dataSource);
}
@Bean(name = "dataSourceDb2")
@ConfigurationProperties("spring.datasource.db2")
public DataSource dataSourceDb2(HikariProperties properties) {
HikariDataSource dataSource = DataSourceBuilder.create().type(HikariDataSource.class).build();
return properties.dataSource(dataSource);
}
@Bean(name = "dataSourceDb3")
@ConfigurationProperties("spring.datasource.db3")
public DataSource dataSourceDb3(HikariProperties properties) {
HikariDataSource dataSource = DataSourceBuilder.create().type(HikariDataSource.class).build();
return properties.dataSource(dataSource);
}
@Bean(name = "dataSourceDb4")
@ConfigurationProperties("spring.datasource.db4")
public DataSource dataSourceDb4(HikariProperties properties) {
HikariDataSource dataSource = DataSourceBuilder.create().type(HikariDataSource.class).build();
return properties.dataSource(dataSource);
}
@Primary
@Bean(name = "dynamicDataSource")
public DynamicDataSource dataSource(DataSource dataSourceDb1, DataSource dataSourceDb2, DataSource dataSourceDb3, DataSource dataSourceDb4) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.DB1.name(), dataSourceDb1);
targetDataSources.put(DataSourceType.DB2.name(), dataSourceDb2);
targetDataSources.put(DataSourceType.DB3.name(), dataSourceDb3);
targetDataSources.put(DataSourceType.DB4.name(), dataSourceDb4);
return new DynamicDataSource(dataSourceDb1, targetDataSources);
}
/**
* @param dataSource
* @Description 创建事务
* @Throws
* @Return org.springframework.jdbc.datasource.DataSourceTransactionManager
* @Date 2023-04-10 15:26:50
* @Author WangKun
*/
@Primary
@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @Description Hikari连接池设置
* @Author WangKun
* @Date 2023/4/4 15:05
* @Version
*/
@Configuration
public class HikariProperties {
@Value("${spring.datasource.hikari.minimumIdle}")
private int minIdle;
@Value("${spring.datasource.hikari.maximumPoolSize}")
private int maxPoolSize;
@Value("${spring.datasource.hikari.idleTimeout}")
private int idleTimeout;
@Value("${spring.datasource.hikari.maxLifetime}")
private int maxLifetime;
@Value("${spring.datasource.hikari.connectionTimeout}")
private int connectionTimeout;
public HikariDataSource dataSource(HikariDataSource dataSource) {
//配置Hikari连接池
dataSource.setConnectionTimeout(connectionTimeout);//连接超时时间设置
dataSource.setIdleTimeout(idleTimeout);//连接空闲生命周期设置
dataSource.setMaximumPoolSize(maxPoolSize);//连接池允许的最大连接数量
dataSource.setMaxLifetime(maxLifetime);//检查空余连接优化连接池设置时间,单位毫秒
dataSource.setMinimumIdle(minIdle);//连接池保持最小空余连接数量
return dataSource;
}
}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* @Description 自定义动态数据源
* @Author WangKun
* @Date 2023/4/4 15:05
* @Version
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
import lombok.extern.slf4j.Slf4j;
/**
* @Description 动态数据源切换处理
* @Author WangKun
* @Date 2023/4/4 11:30
* @Version
*/
@Slf4j
public class DynamicDataSourceContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量,
* 所以每一个线程都可以独立地改变自己,而不会影响其它线程。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* @param dsType
* @Description 设置数据源的变量
* @Throws
* @Return void
* @Date 2023-04-04 11:30:21
* @Author WangKun
*/
public static void setDataSourceType(String dsType) {
CONTEXT_HOLDER.set(dsType);
log.info("连接到{}数据源", dsType);
}
/**
* @param
* @Description 获得数据源的变量
* @Throws
* @Return java.lang.String
* @Date 2023-04-04 11:30:45
* @Author WangKun
*/
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
/**
* @param
* @Description 清空数据源变量
* @Throws
* @Return void
* @Date 2023-04-04 11:30:51
* @Author WangKun
*/
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
/**
* @Description 数据源枚举
* @Author WangKun
* @Date 2023/4/4 11:23
* @Version
*/
public enum DataSourceType {
/**数据源1 */
DB1,
/**数据源2 */
DB2,
/**数据源3 */
DB3,
/**数据源4 */
DB4
}
调用: