目录结构:
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
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
import lombok.extern.slf4j.Slf4j; /** * @Description 动态数据源切换处理 * @Author WangKun * @Date 2023/4/4 11:30 * @Version */ @Slf4j public class DynamicDataSourceContextHolder { /** * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量, * 所以每一个线程都可以独立地改变自己,而不会影响其它线程。 */ private static final ThreadLocalCONTEXT_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 }
调用: