IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot_aop_动态(多)数据源 -> 正文阅读

[Java知识库]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();
        }
    }

}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:17:51  更:2022-03-10 22:19:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:45:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码