springboot_aop_动态(多)数据源

多数据源配置
# 主库数据源配置
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.url=jdbc:mysql://192.168.1.11:3308/redenvelopes?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&allowMultiQueries=true
spring.datasource.master.username=redenvelopes
spring.datasource.master.password=USSfro2Y2vSoxj4nOH8JA4zk9tmINs7i6bOKQgcu0cQ1SFHP74CIGdHzsxZaQlDAHxRs7MMkj1ilDeLAnOBBzw==
spring.datasource.master.initial-size=30
spring.datasource.master.max-active=300
spring.datasource.master.min-idle=20
spring.datasource.master.time-between-eviction-runs-millis=60000
spring.datasource.master.min-evictable-idle-time-millis=300000
spring.datasource.master.validation-query=select 1
spring.datasource.master.test-while-idle=true
spring.datasource.master.test-on-borrow=true
spring.datasource.master.test-on-return=false
spring.datasource.master.max-wait=60000
spring.datasource.master.filters=stat,config
spring.datasource.master.filter.stat.enabled=true
spring.datasource.master.filter.stat.slow-sql-millis=100000
spring.datasource.master.filter.stat.log-slow-sql=true
spring.datasource.master.connection-properties=config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALPq2OsSVb075O3ELXlC82KF2YdKtVv01y4tu7wZN11b3mw6zZDIdh+ZRU3S+Kui6wVE7XRsupbroIJcspW40eMCAwEAAQ==
# 从库数据源配置
spring.datasource.readonly.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.readonly.url=jdbc:mysql://192.168.1.11:3308/redenvelopes?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.readonly.username=devsup01
spring.datasource.readonly.password=DaSw+Rg9zM508O+H5b2a32mE0m6Qy1v2YtSl0ti6QTtJOXjKWsQhkdCDIRiK9gV1/hY7hYrpGe403yom+um4PQ==
spring.datasource.readonly.initial-size=30
spring.datasource.readonly.max-active=300
spring.datasource.readonly.min-idle=20
spring.datasource.readonly.time-between-eviction-runs-millis=60000
spring.datasource.readonly.min-evictable-idle-time-millis=300000
spring.datasource.readonly.validation-query=select 1
spring.datasource.readonly.test-while-idle=true
spring.datasource.readonly.test-on-borrow=true
spring.datasource.readonly.test-on-return=false
spring.datasource.readonly.max-wait=60000
spring.datasource.readonly.filters=stat,config
spring.datasource.readonly.filter.stat.enabled=true
spring.datasource.readonly.filter.stat.slow-sql-millis=100000
spring.datasource.readonly.filter.stat.log-slow-sql=true
spring.datasource.readonly.connection-properties=config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMzWx3SAo7gF2TguEJQjL5TFON47dTAJCU0Va3oygJ34OJRHjlPeOcla7eaxp40DlhH/MprUlgmLWCvXNEtEL8UCAwEAAQ==

配置DataSource

/**
 * 动态数据源配置
 * <p>
 * 动态数据源需要关闭SpringBoot默认数据源,
 * 关闭方法:@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
 */
@Configuration
public class DataSourceConfig {

    public final static String DS_MASTER = "master";

    public final static String DS_READONLY = "readonly";

    protected Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 主库数据源
     */
    @Bean(name = DS_MASTER)
    @ConfigurationProperties(prefix = "spring.datasource.master") // application.properteis中对应属性的前缀
    public DataSource dataSource1() {
        logger.info("Load datasource of {} ...", DS_MASTER);
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 备库数据源
     */
    @Bean(name = DS_READONLY)
    @ConfigurationProperties(prefix = "spring.datasource.readonly") // application.properteis中对应属性的前缀
    public DataSource dataSource2() {
        logger.info("Load datasource of {} ...", DS_READONLY);
        return DruidDataSourceBuilder.create().build();
    }


    @Autowired
    @Lazy
    @Qualifier(DS_MASTER)
    DataSource master;

    @Autowired
    @Lazy
    @Qualifier(DS_READONLY)
    DataSource readonly;

    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     */
    @Bean
    @Primary
    public DataSource dataSource() {
        RdpRoutingDataSource rdpRoutingDataSource = new RdpRoutingDataSource();
        // 默认数据源
        rdpRoutingDataSource.setDefaultTargetDataSource(master);
        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap<>(5);
        dsMap.put(DS_MASTER, master);
        dsMap.put(DS_READONLY, readonly);

        rdpRoutingDataSource.setTargetDataSources(dsMap);
        return rdpRoutingDataSource;
    }
动态选择DataSource

动态数据源线程上下文

/**
 * 数据源切换上下文.线程安全
 * 
 * @author Dhui.huang
 * @date 2019-04-02
 */
public class DataSourceContextHolder {
   
   protected static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);

    /**
     * 默认数据源
     */
    public static final String DEFAULT_DS = "master";

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源
     * 某些场景数据源切换并不会生效,以RdpRoutingDataSource.determineCurrentLookupKey日志输出为准
     * eg: 先开启事务再切换数据源,切换不会生效。务必先切换数据源再开户事务
     * 
     * @param dbType
     */
    public static void setDB(String dbType) {
       logger.debug("Try to select datasource:{}", dbType); 
        contextHolder.set(dbType);
    }

    // 获取数据源名
    public static String getDB() {
        return (contextHolder.get());
    }

    // 清除数据源
    public static void clearDB() {
       logger.debug("Try to clear datasource:{}", getDB()); 
        contextHolder.remove();
    }
}

自定义类继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey(),返回数据源的唯一标识;

public class RdpRoutingDataSource extends AbstractRoutingDataSource {
	
	protected static Logger logger = LoggerFactory.getLogger(RdpRoutingDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
    	logger.debug("当前数据源: {}", StringUtils.isEmpty(DataSourceContextHolder.getDB())? "default" : DataSourceContextHolder.getDB());
        return DataSourceContextHolder.getDB();
    }
}
注解(主动配置选择)

用于主动选择数据源

@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.METHOD
})
//@Inherited
public @interface DsSelector {
    String value() default DataSourceContextHolder.DEFAULT_DS;
}
AOP 拦截切换数据源

每次对@DsSelector注解的方法去数据源上下文中获取当前线程的连接的数据源

/**
 * 数据源切换切面
 * @author lihao
 */

@EnableAspectJAutoProxy
@Order(1)
@Aspect
@Component
public class RdpDataSourceAspect {
    private Logger logger = LoggerFactory.getLogger(RdpDataSourceAspect.class);

	@Around("execution(* cn.swiftpass..service.impl.*.*(..)) && @annotation(DsSelector)")
    public Object around(ProceedingJoinPoint joinPoint){
	    try {
            String dataSource = "";
            // 当前class
            Class<?> className = joinPoint.getTarget().getClass();
            // 当前方法名
            String methodName = joinPoint.getSignature().getName();
            // 方法的参数的类型
            Class<?>[] argClass = ((MethodSignature)joinPoint.getSignature()).getParameterTypes();

            try {
                // 方法对象
                Method method = className.getMethod(methodName, argClass);
                // 判断是否存在@DsSelector注解
                if (method.isAnnotationPresent(DsSelector.class)) {
                    DsSelector annotation = method.getAnnotation(DsSelector.class);
                    // 取出指定的数据源名
                    dataSource = annotation.value();
                }
            } catch (Exception e) {
                logger.warn("RdpDataSourceAspect.around exception", e);
            }
            // 切换数据源
            DataSourceContextHolder.setDB(dataSource);
            return joinPoint.proceed();
        } catch (Throwable t) {
            logger.error("未知异常", t);
            return null;
        } finally {
            // 清楚当前线程的数据源
            DataSourceContextHolder.clearDB();
        }
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值