SpringBoot+AOP实现动态切换数据源
首先描述下笔者的基本步骤:
1.yml配置文件中定义多数据源;
2.自定义一个注解类(@DataSource 可标注在类或方法上),定义String类型的变量value,value中存储数据源名称;
3.创建子类(MyThreadLocal),定义一个ThreadLocal类型的静态变量,用来存储第2条中@DataSource注解定义的value变量的值,并且创建方法来获取、移除ThreadLocal中value值;
4.创建一个切面类,定义切点(添加了@DataSource注解的类或方法),使用环绕通知,在原方法执行前,将@DataSource注解的value值存入ThreadLocal静态变量中 => 执行原方法 => 移除ThreadLocal中value值;
5.创建一个类(DruidProperties),读取配置文件中数据源信息,并生成多套数据源配置,用Map<Object,Object>存储;
6.创建一个类,继承AbstractRoutingDataSource类(这个类可以实现动态数据源切换),重写determineCurrentLookupKey()方法,指定要切换数据源的key,在子类构造方法中注入所有数据源。
1.导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.11</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
2.yml配置
server:port: 8081 spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverds:master: #数据源1url: jdbc:mysql://192.168.192.129:3306/test01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: 123456slave: #数据源2url: jdbc:mysql://192.168.192.129:3306/test02?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: 123456initialSize: 5minIdle: 10maxActive: 20maxWait: 60000
3.自定义注解@DataSource
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})//作用在类和方法上
// @Inherited//当以后我们在定义一个作用于类的注解时候,如果希望该注解也作用于其子类,那么可以用@Inherited 来进行修饰。
public @interface DataSource {String value() default DataSourceName.DEFAULT_DATASOURCE_NAME;
}
value默认值为常量("master")
public interface DataSourceName {String DEFAULT_DATASOURCE_NAME="master";String OTHER_DATASOURCE_NAME="slave";
}
4.创建类,声明ThreadLocal静态变量,并编写存储、获取、移除方法
public class MyThreadLocal {//ThreadLocal 为每一个线程都提供一份变量的副本,从而实现同时访问而互不相干扰private static ThreadLocal<String> threadLocal=new ThreadLocal<>();/*** set(T value):设置线程本地变量的内容。* get():获取线程本地变量的内容。* remove():移除线程本地变量。注意在线程池的线程复用场景中在线程执行完毕时一定要调用remove,避免在线程被重新放入线程池中时被本地变量的旧状态仍然被保存。* @param dsType*///存储数据源名称public static void setDataSourceName(String dataSourceName){threadLocal.set(dataSourceName);}//获取当前线程所使用的数据源名称public static String getDataSourceName(){return threadLocal.get();}//移除当前线程存储的数据源名称public static void removeDataSourceName(){threadLocal.remove();}
}
5.创建切面类,拦截@DataSource标注的方法或类
@Aspect
@Component
public class DataSourceAspect {//定义切点,拦截@DataSource标注的类和方法@Pointcut("@annotation(com.xxx.ds.annotation.DataSource)||@within(com.xxx.ds.annotation.DataSource)")public void pointCut(){}@Around("pointCut()")public Object useMyDataSource(ProceedingJoinPoint pjp){// 获取注解中数据源名称// 1.获取当前方法MethodSignature methodSignature = (MethodSignature) pjp.getSignature();// 2.获取当前方法上@DataSource的值,若方法上不存在,查找类上的值DataSource dataSource=getDataSource(methodSignature);// 3.若注解不为空,获取值,并存入本地线程if(dataSource!=null){String value = dataSource.value();//将值存入ThreadLocal中MyThreadLocal.setDataSourceName(value);}//4.执行原方法,有返回值直接返回try {return pjp.proceed();} catch (Throwable e) {e.printStackTrace();}finally {//5.移除本地线程的值MyThreadLocal.removeDataSourceName();}return null;}private DataSource getDataSource(MethodSignature methodSignature) {//查找方法上的@DataSource注解 AnnotationUtils.findAnnotation(Method,annotationType)DataSource annotation = AnnotationUtils.findAnnotation(methodSignature.getMethod(), DataSource.class);if(annotation!=null){//方法上有值,直接返回return annotation;}//如果方法中没有@DataSource注解,查找类上的@DataSource注解 AnnotationUtils.findAnnotation(Class,annotationType)return AnnotationUtils.findAnnotation(methodSignature.getDeclaringType(), DataSource.class);}
}
6.创建类,读取yml配置文件中的值,并存储所有数据源
@Component
@ConfigurationProperties("spring.datasource")
public class DruidProperties {private String type;private String driverClassName;private Map<String,Map<String,String>> ds;private Integer initialSize;private Integer minIdle;private Integer maxActive;private Integer maxWait;//将yml中所有数据源加载到DruidDataSource中public Map<Object,Object> loadAllDataSource(){Map<Object, Object> map = new HashMap<>();try {for (Map.Entry<String, Map<String, String>> entry : ds.entrySet()) {//map中的内容为:{master={Datasource{xxx=xxx,xxx=xxx},salver={DataSource{xxx=xxx,xxx=xxx}}}}/*** 将数据源名称设置为主键,(url、username、password构造DruidSource对象,将DruidSource传入initDataSource方法,初始化DataSource)*/map.put(entry.getKey(),initDataSource((DruidDataSource) DruidDataSourceFactory.createDataSource(entry.getValue())));}} catch (Exception e) {e.printStackTrace();}return map;}public DataSource initDataSource(DruidDataSource druidDataSource){druidDataSource.setDbType(type);druidDataSource.setDriverClassName(driverClassName);druidDataSource.setInitialSize(initialSize);druidDataSource.setMinIdle(minIdle);druidDataSource.setMaxActive(maxActive);druidDataSource.setMaxWait(maxWait);return druidDataSource;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public Map<String, Map<String, String>> getDs() {return ds;}public void setDs(Map<String, Map<String, String>> ds) {this.ds = ds;}public Integer getInitialSize() {return initialSize;}public void setInitialSize(Integer initialSize) {this.initialSize = initialSize;}public Integer getMinIdle() {return minIdle;}public void setMinIdle(Integer minIdle) {this.minIdle = minIdle;}public Integer getMaxActive() {return maxActive;}public void setMaxActive(Integer maxActive) {this.maxActive = maxActive;}public Integer getMaxWait() {return maxWait;}public void setMaxWait(Integer maxWait) {this.maxWait = maxWait;}
}
7.创建类继承AbstractRoutingDataSource,获取ThreadLocal中的数据源名称,动态加载
@Component
public class MyRoutingDataSource extends AbstractRoutingDataSource {public MyRoutingDataSource(DruidProperties druidProperties) {//1.设置所有数据源super.setTargetDataSources(druidProperties.loadAllDataSource());//2.设置默认数据源
super.setDefaultTargetDataSource(druidProperties.loadAllDataSource().get(DataSourceName.DEFAULT_DATASOURCE_NAME));super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {//根据ThreadLocal中数据源名称选择数据源System.out.println("线程中的值为: "+MyThreadLocal.getDataSourceName());return MyThreadLocal.getDataSourceName();}
}
8.在业务方法上添加@DataSource注解,并制定value值,实现动态切换
最后附上该demo地址:
dynamicDataSource2: SpringBoot+AOP实现动态切换数据源
SpringBoot+AOP实现动态切换数据源相关推荐
- springboot+mybatis实现动态切换数据源
目前有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源. 如何在spring框架中解决多数据源的问题 使用springboot 整合多数据源 遇到的坑 1.添加依赖 < ...
- springmvc+mybatis多数据源配置,AOP注解动态切换数据源
springmvc与springboot没多大区别,springboot一个jar包配置几乎包含了所有springmvc,也不需要繁琐的xml配置,springmvc需要配置多种jar包,需要繁琐的x ...
- Spring Boot多数据源配置并通过注解实现动态切换数据源
文章目录 1. AbstractRoutingDataSource类介绍 2. ThreadLocal类介绍 3. 环境准备 3.1 数据库准备 3.2 项目创建 4. 具体实现 4.1 定义数据源枚 ...
- springboot 中动态切换数据源(多数据源应用设计)
目录 原理图 数据库 项目结构 启动类 entity controller service mapper 配置文件 线程上下文 (DataSourceHolder) 动态数据源 DynamicData ...
- Spring学习总结(16)——Spring AOP实现执行数据库操作前根据业务来动态切换数据源
深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...
- Springboot/MybatisPlus动态切换数据源
1.1 简述 最近项目中有动态切换数据源需求,主要是要动态切换数据源进行数据的获取,现将项目中实现思路及代码提供出来,供大家参考.当然切换数据源还有其他的方式比如使用切面的方式,其实大体思路是一样的. ...
- mybatis手动切换数据库_在Spring项目中使用 Mybatis 如何实现动态切换数据源
在Spring项目中使用 Mybatis 如何实现动态切换数据源 发布时间:2020-11-17 16:20:11 来源:亿速云 阅读:108 作者:Leah 这篇文章将为大家详细讲解有关在Sprin ...
- Java实现动态切换数据源
在一般的Java项目中,如果使用Spring去管理数据库连接信息,一般只能连接一个数据库,可是会有部分情况我们需要连接多个数据库,甚至还会存在不同的请求需要根据配置信息连接不同的数据库,比如: 在很多 ...
- spring boot 动态切换数据源实现多租户开发
之前的文章有介绍过spring boot 动态切换数据源spring boot 动态切换数据源(数据源信息从数据库中读取)_lgq2016的博客-CSDN博客,今天简单介绍一下动态数据源切换实战,主要 ...
最新文章
- 【机器学习】中国大学慕课《机器学习》课后习题(二)(回归)
- WPF 使用皮肤影响按钮自定义
- java listutils_Java的list自定义工具类ListUtils
- OpenCV中的凸包
- 华师大数据科学考研_21考研择校 | 华东师范大学软件工程怎么样?
- python restful风格_总结python bottle框架支持jquery ajax的RESTful风格的PUT和DELETE方法
- oracle ash dump 导出,Oracle 导出 ASH的dump信息
- VBA EXCEL 常用代码
- 皮尔逊相关系数,斯皮尔曼等级相关系数,(易错!!)假设检验 ,SPSS
- 很遗憾,该服务器不支持 jmail 组件!,jmail组件注册以及权限设置问题
- c++ open_file函数
- 匿名访问ftp服务器
- 服务(Service)
- Array Shrinking(CodeForces - 1312E )
- Oracle EBS 查找段限定词中账户类型SQL
- 【注释说明】pcl库中OBB和AABB包围盒头文件pcl/features/moment_of_inertia_estimation.h,中文翻译注释说明,欢迎补充
- math_数集(数集符号)/算数运算中英文对照
- 计算机的音量打不开,电脑静音,音量打不开.怎么办?
- 智能家居电动窗帘如何选择?小米,绿米还是智汀
- 锂电池和锂离子电池的区别