首先描述下笔者的基本步骤:

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实现动态切换数据源相关推荐

  1. springboot+mybatis实现动态切换数据源

    目前有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源. 如何在spring框架中解决多数据源的问题 使用springboot 整合多数据源 遇到的坑 1.添加依赖 < ...

  2. springmvc+mybatis多数据源配置,AOP注解动态切换数据源

    springmvc与springboot没多大区别,springboot一个jar包配置几乎包含了所有springmvc,也不需要繁琐的xml配置,springmvc需要配置多种jar包,需要繁琐的x ...

  3. Spring Boot多数据源配置并通过注解实现动态切换数据源

    文章目录 1. AbstractRoutingDataSource类介绍 2. ThreadLocal类介绍 3. 环境准备 3.1 数据库准备 3.2 项目创建 4. 具体实现 4.1 定义数据源枚 ...

  4. springboot 中动态切换数据源(多数据源应用设计)

    目录 原理图 数据库 项目结构 启动类 entity controller service mapper 配置文件 线程上下文 (DataSourceHolder) 动态数据源 DynamicData ...

  5. Spring学习总结(16)——Spring AOP实现执行数据库操作前根据业务来动态切换数据源

    深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...

  6. Springboot/MybatisPlus动态切换数据源

    1.1 简述 最近项目中有动态切换数据源需求,主要是要动态切换数据源进行数据的获取,现将项目中实现思路及代码提供出来,供大家参考.当然切换数据源还有其他的方式比如使用切面的方式,其实大体思路是一样的. ...

  7. mybatis手动切换数据库_在Spring项目中使用 Mybatis 如何实现动态切换数据源

    在Spring项目中使用 Mybatis 如何实现动态切换数据源 发布时间:2020-11-17 16:20:11 来源:亿速云 阅读:108 作者:Leah 这篇文章将为大家详细讲解有关在Sprin ...

  8. Java实现动态切换数据源

    在一般的Java项目中,如果使用Spring去管理数据库连接信息,一般只能连接一个数据库,可是会有部分情况我们需要连接多个数据库,甚至还会存在不同的请求需要根据配置信息连接不同的数据库,比如: 在很多 ...

  9. spring boot 动态切换数据源实现多租户开发

    之前的文章有介绍过spring boot 动态切换数据源spring boot 动态切换数据源(数据源信息从数据库中读取)_lgq2016的博客-CSDN博客,今天简单介绍一下动态数据源切换实战,主要 ...

最新文章

  1. 【机器学习】中国大学慕课《机器学习》课后习题(二)(回归)
  2. WPF 使用皮肤影响按钮自定义
  3. java listutils_Java的list自定义工具类ListUtils
  4. OpenCV中的凸包
  5. 华师大数据科学考研_21考研择校 | 华东师范大学软件工程怎么样?
  6. python restful风格_总结python bottle框架支持jquery ajax的RESTful风格的PUT和DELETE方法
  7. oracle ash dump 导出,Oracle 导出 ASH的dump信息
  8. VBA EXCEL 常用代码
  9. 皮尔逊相关系数,斯皮尔曼等级相关系数,(易错!!)假设检验 ,SPSS
  10. 很遗憾,该服务器不支持 jmail 组件!,jmail组件注册以及权限设置问题
  11. c++ open_file函数
  12. 匿名访问ftp服务器
  13. 服务(Service)
  14. Array Shrinking(CodeForces - 1312E )
  15. Oracle EBS 查找段限定词中账户类型SQL
  16. 【注释说明】pcl库中OBB和AABB包围盒头文件pcl/features/moment_of_inertia_estimation.h,中文翻译注释说明,欢迎补充
  17. math_数集(数集符号)/算数运算中英文对照
  18. 计算机的音量打不开,电脑静音,音量打不开.怎么办?
  19. 智能家居电动窗帘如何选择?小米,绿米还是智汀
  20. 锂电池和锂离子电池的区别

热门文章

  1. Dubbo分布式日志追踪
  2. IBM展望未来5年五大科技趋势:“读心术”成真
  3. camera前置摄像头左右镜像问题
  4. 【java基础:FileWriter】方式一:创建文本文件并写入字符内容
  5. win11右键菜单改回原版win10最简单操作方法
  6. 啊,嗯,唔,啊,啊,啊,
  7. 语义分割标签处理RBG 到gray再转回RGB(通用)
  8. ISO 光盘镜像 启动系统
  9. webbuilder连接mysql_Webbuilder学习指导书.doc
  10. 现场教学 | 上国会金融EMBA项目《股东价值创造与财务报表分析》走进校友企业