文章目录

  • 前期准备工作
  • 集成动态数据源模块
    • 1. 步骤一 配置多数据源信息
    • 2.步骤二,扩展Spring的AbstractRoutingDataSource抽象类
    • 3.步骤三-配置DataSource,指定数据源的信息
    • 4.步骤四,通过注解,实现多数据源

前期准备工作

新建一个SpringBoot的项目,暂时先导入两个必要的包

当然还有导入一些其他的包,这里会用到aop,mybatis plus, druid 等,在pom文件中添加以上的依赖:

<!--aop-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency><!--mybatis plus-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.1</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency><!--druid-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version>
</dependency>

然后可以用代码生成工具生成简单的一条线entity,controller,service,dao,mapper,把项目目录结构先搞出来;
这是当前demo的目录结构,datasource那个包是做多数据源处理的,先建上;

项目一开始启动时扫描不到controller和service,这里在启动类中加入@ComponentScan(“包名”)
启动类:DatasourceDemoApplication


@MapperScan("com.example.dao")
@ComponentScan("com.example.controller")
@ComponentScan("com.example.service")
@SpringBootApplication
public class DatasourceDemoApplication {public static void main(String[] args) {SpringApplication.run(DatasourceDemoApplication.class, args);}}

集成动态数据源模块

  • 步骤1,在spring boot中,增加多数据源的配置
  • 步骤2,扩展Spring的AbstractRoutingDataSource抽象类,
    AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现多数据
    源的核心,并对该方法进行Override
  • 步骤3,配置DataSource,指定数据源的信息
  • 步骤4,通过注解,实现多数据源
  • 步骤5、配置加上(exclude={DataSourceAutoConfiguration.class})

然后按着上面的步骤来吧~

1. 步骤一 配置多数据源信息

在yml配置文件中添加数据源的信息,这里我复制了renren_fast的数据库,更名为renren_fast2,然后将renren_fast2中sys_user表中id为1的用户名改为了admin_456.

datasource-demo\src\main\resources\application.yml

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.jdbc.Driverdruid:first:url: jdbc:mysql://localhost:3306/renren_fast?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8username: rootpassword: 123456second:url: jdbc:mysql://localhost:3306/renren_fast2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8username: rootpassword: 123456

我们之前建好了datasource的包,这里开始做多数据源部分的处理了;
首先要编写配置类,配置多数据源信息;

/*** 配置多数据源* @author 天赋吉运-yxh* @create 2019-01-22 15:31*/
@Configuration
public class DynamicDataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.druid.first")public DataSource firstDataSource(){return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.druid.second")public DataSource secondDataSource(){return DruidDataSourceBuilder.create().build();}}

另外,将步骤五的处理提前做了,因为我们的数据源是自己生成的,所以要去掉原型springboot启动时候自动装配的数据源配置。然后倒入自己写的配置。
在启动类添加以下注解:

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@Import({DynamicDataSourceConfig.class})

2.步骤二,扩展Spring的AbstractRoutingDataSource抽象类

package com.example.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;/*** 动态数据源* @author 天赋吉运-杨晓慧* @create 2019-01-22 15:55*/public class DynamicDataSource extends AbstractRoutingDataSource {@Override// determineCurrentLookupKey()决定使用哪个数据源protected Object determineCurrentLookupKey() {return getDataSource();}
}

为了方便我们使用aop注解时候的得到的lookupkey参数能传递到这里。所以需要建一个线程安全的ThreadLocal变量,用于传参,避免复杂的参数传递过程。那么,我们获取这个lookupkey就从这个ThreadLocal里面去获取,所以determineCurrentLookupKey()直接返回getDataSource()。

/*** 动态数据源* determineCurrentLookupKey()决定使用哪个数据源* @author 天赋吉运-杨晓慧* @create 2019-01-22 15:55*/public class DynamicDataSource extends AbstractRoutingDataSource {/*** ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程内的变量独立于其他线程里的变量* 即 ThreadLocal可以为每个线程创建一个【单独的变量副本】* 相当于线程的private static类型变量*/private static final ThreadLocal<String> contextHolder =new ThreadLocal<>();@Overrideprotected Object determineCurrentLookupKey() {return getDataSource();}public static void setDataSource(String dataSource){contextHolder.set(dataSource);}public static String getDataSource(){return contextHolder.get();}public static void clearDataSource(){contextHolder.remove();}//构造方法//决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,Object> targetDataSources){super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}
}

但是仅做上面步骤还不够,因为自动装载数据源的这个过程我们在前面已经去掉了,所以需要我们自己手动去装配数据源的信息。调用determineCurrentLookupKey()方法的determineTargetDataSource()方法可以看到,里面有用到一些变量,比如resolvedDataSources,它是一个map,查看调用地方可以看到,它的初始化实在afterPropertiesSet()方法中,这初始化方法会用到一些变量,比如:targetDataSources、defaultTargetDataSource,然后你回发现,这个两个参数,都是一个set方法初始化的,setTargetDataSources(Map<Object, Object> targetDataSources)、setDefaultTargetDataSource(Object defaultTargetDataSource),这两个就是所有数据源信息,和默认数据源信息,所以我们需要手动把我们配置的数据源信息set进去。
大家可以仔细看看org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource的源码

3.步骤三-配置DataSource,指定数据源的信息

确认最终使用哪个数据源的方法有很多,因为涉及到一个顺序问题,调用determineCurrentLookupKey()前一定要把数据源的信息初始化化好,所以我们可以写一个DynamicDataSource的构造方法。两个参数,一个默认数据源,一个所有数据源。然后调用afterPropertiesSet(),初始化必要的参数。

所以在DynamicDataSource类中增加一个构造函数,初始化必要的参数

     //构造方法//决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,Object> targetDataSources){super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}

因为这数据源的信息启动时候就需要初始化,因为后面事务等类的初始化都需要依赖数据源bean,所以在DynamicDataSourceConfig配置中,我们生成一个DynamicDataSource的bean。
在DynamicDataSourceConfig配置类中增加一个bean

    /*** 因为数据源的信息启动时候就需要初始化,后面事务等类的初始化都需要依赖数据源bean,所以在DynamicDataSourceConfig中,我们生成了一个DynamicDataSource的bean*/@Bean@Primarypublic DynamicDataSource dataSource(DataSource firstDataSource,DataSource secondDataSource){Map<Object,Object> targetDataSources=new HashMap<>();targetDataSources.put(DataSourceNames.FIRST,firstDataSource);targetDataSources.put(DataSourceNames.SECOND,secondDataSource);return new DynamicDataSource(firstDataSource,targetDataSources);}

@Primary 优先考虑,优先考虑被注解的对象注入。

数据源的名称经常会用到,我这里定义了一个枚举类

package com.example.datasource;/*@author 天赋吉运-yxh@数据源名称@create 2019/1/23
*/public interface DataSourceNames {String FIRST = "first";String SECOND = "second";
}

通过上面的配置,我们可以说已经完成了手动装配我们自定义的多数据源的过程了。接下来的工作就是我们自己去指定ThreadLocal里面的值就行。

4.步骤四,通过注解,实现多数据源

我们采用aop的方式,在需要修改数据源的地方使用注解方式去切换,然后切面修改ThreadLocal的内容。这里比较简单,我就直接贴代码:

package com.example.datasource;import java.lang.annotation.*;/*@author 天赋吉运-yxh@多数据源注解@create 2019/1/22
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {// 采用AOP的方式,在需要修改数据源的地方使用注解的方式,去切换,然后通过切面修改ThreadLocal的内容String name() default "";
}

切面处理逻辑

package com.example.datasource;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.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** 多数据源,切面处理类* @author 天赋吉运-杨晓慧* @create 2019-01-22 17:31*/
@Aspect
@Component
public class DataSourceAspect implements Ordered{protected org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());//设置切点@Pointcut("@annotation(com.example.datasource.DataSource)")public void  dataSourcePointCut(){}//切面@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method =signature.getMethod();DataSource ds=method.getAnnotation(DataSource.class);if (ds ==null) {// 当数据源为空时 配置默认数据源为第一个DynamicDataSource.setDataSource(DataSourceNames.FIRST);logger.debug("set datasource is "+ DataSourceNames.FIRST);} else  {// 根据既定已配置数据源来设定DynamicDataSource.setDataSource(ds.name());logger.debug("set datasource is " + ds.name());}try {return point.proceed();} finally {DynamicDataSource.clearDataSource();logger.debug("clean datasource");}}@Overridepublic int getOrder() {return 1;}
}

这是选择的目录结构呢

最后收工,测试一把吧;
在service中定义两个接口,实现的时候分别查询不同的数据源

/*** <p>* 系统用户 服务类* </p>** @author Hepburn_Yang* @since 2019-01-22*/
public interface SysUserService extends  IService<SysUser> {SysUser findUserByFirstDb(long id);SysUser findUserBySecondDb(long id);}

添加实现

/*** <p>* 系统用户 服务实现类* </p>** @author Hepburn_Yang* @since 2019-01-22*/
@Service("sysUserService")
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> implements SysUserService {@Override// 不设定数据源,连接默认数据库,(默认是firstDb)public SysUser findUserByFirstDb(long id) {return this.baseMapper.selectById(id);}@DataSource(name = DataSourceNames.SECOND)@Override// 设置数据源为secondDb,看上面的@DataSourcepublic SysUser findUserBySecondDb(long id) {return this.baseMapper.selectById(id);}
}

走一波单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DatasourceDemoApplicationTests {@Resourceprivate SysUserService sysUserService;@Testpublic void test(){SysUser user1 =sysUserService.findUserByFirstDb(1);System.out.println("first DB ------------->"+user1.toString());SysUser user2 = sysUserService.findUserBySecondDb(1);System.out.println("second DB ------------>" + user2.toString());}
}

来看看日志输出:

收工收工,拜拜了,有问题请留言~~

SpringAop动态数据源实现相关推荐

  1. 手撸一个动态数据源的Starter 完整编写一个Starter及融合项目的过程 保姆级教程

    手撸一个动态数据源的Starter! 文章目录 手撸一个动态数据源的Starter! 前言 一.准备工作 1,演示 2,项目目录结构 3,POM文件 二.思路 三.编写代码 1,定义核心注解 Ds 2 ...

  2. Spring Boot + Mybatis 配合 AOP 和注解实现动态数据源切换配置

    Spring Boot + Mybatis 配合 AOP 和注解实现动态数据源切换配置 前言: 1. 数据库准备: 2. 环境准备: 3.代码部分 4. 测试: 5.等等 6.配合注解实现 7 .测试 ...

  3. nacos动态配置数据源_Jasper 怎么配置动态数据源

    Jasper 本身是不支持动态数据源的,能用的解决方式是通过 api 自定义数据源,实际操作就是根据条件判断后动态设定 jdbc 的 url.用户名及密码等连接属性.比如: String userNa ...

  4. Spring 下,关于动态数据源的事务问题的探讨

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:青石路 cnblogs.com/youzhibing ...

  5. Spring Boot 动态数据源(多数据源自己主动切换)

    本文实现案例场景: 某系统除了须要从自己的主要数据库上读取和管理数据外.另一部分业务涉及到其它多个数据库,要求能够在不论什么方法上能够灵活指定详细要操作的数据库. 为了在开发中以最简单的方法使用,本文 ...

  6. SpringBoot+Mybatis 实现动态数据源切换方案

    背景 最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据.考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态数据源的解决方案.在此分享给大家. 实现方案 数据库配置文 ...

  7. nacos windows部署_Sentinel-Go 集成 Nacos 实现外部动态数据源

    导读:2020年,Sentinel 推出 Go 原生版本Sentinel-Golang,在云原生领域继续突破.本文将从实际出发 结合案例说明 在Sentinel-Golang中如何集成Nacos,使其 ...

  8. Echarts图表在VUE项目中使用动态数据源

    动态数据源问题:目前我使用的方法是当后台查询到数据时,直接传递给option中的对应data内: 例如柱状图,可以直接将x轴数据注入到 this.option.xAxis.data 中,如 this. ...

  9. Sentinel-Go 集成 Nacos 实现外部动态数据源

    **导读:**2020年,Sentinel 推出 Go 原生版本Sentinel-Golang,在云原生领域继续突破.本文将从实际出发 结合案例说明 在Sentinel-Golang中如何集成Naco ...

最新文章

  1. Hdu 1029 Ignatius and the Princess IV
  2. MySQL_常见函数
  3. 在中国做操作系统研发 20 年是种什么体验?
  4. windows下,‘nmake‘不是内部或外部命令,也不是可运行的程序或批处理文件
  5. 计算机基础知识教程职称怎么计算,2017年职称计算机考试基础知识教程详解(二十一)...
  6. 执行环境及作用域分析
  7. 点击头像上传文件的效果
  8. 如何删除gmail快捷链接?
  9. AES加密算法|密码学|网络空间安全
  10. maven使用java8
  11. X-Frame-Options
  12. 创建glance镜像报错HTTP503
  13. 【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD登录并获取AccessToken -- cca.acquireTokenByCode(tokenRequest)
  14. 数控技术转行java_我29岁想转行数控却找不到工作
  15. 瑞芯微RK3036无线同屏器芯片处理器介绍
  16. 产品、开发、UI等技术部门常用工具
  17. 使用 Visio 2002 组织结构图解决方案
  18. 京东数字科技集团大数据部总经理张旭:数据资产管理
  19. 猪八戒网创始人朱明跃:蛰伏9年明白三件事
  20. Ubuntu18.04安装gup+cuda11.0+cudnn8.0.5

热门文章

  1. 文件上传(upload)
  2. 医学遗传学词汇英语术语英文(Glossary) 7
  3. java OA 办公系统 模块设计方案
  4. 《Graph-MLP Node Classification without Message Passing in Graph》阅读笔记
  5. 帕拉迪“聚力赋能 创势前行”,IAM击破企业安全三大痛点
  6. 2.2.3 电阻的近似串联分压
  7. Excel(WPS部分功能不适用)技巧:快速制作工资条
  8. 热点前瞻:砷化镓+电子证照+证券板块+化工原料
  9. 生产制造行业中智邦国际ERP的五大应用
  10. shell if 多条件判断 时间大小比较