一、引入依赖

  引入数据库连接池的依赖——druid和面向切面编程的依赖——aop,如下所示:

        <!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、创建数据库

  1、主数据库

  使用前文中已经创建的名为spring_boot_demo的数据库。

  spring_boot_demo中t_user数据如下:

  

  2、辅数据库

  数据库名为other_data,库中建立数据表t_user,表结构与spring_boot_demo中的t_user一致。

  实际项目中,大多是跨数据库的数据源切换,常用在同公司的多个不同系统中共用一个用户数据库,或者二次开发项目在原有数据库基础上做拓展,保留原有的数据连接。

  这里为了方便操作,就都在mysql下部署数据库并且使表结构一致,方便形成数据对比。

  other_data中插入数据如下:

  

三、修改数据库连接配置信息

  在application.yml中,修改数据库连接配置如下:

spring:
application:
name: spring-boot-demo
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
primary:
url: jdbc:mysql://localhost:3306/spring_boot_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driverClassName: com.mysql.jdbc.Driver
second:
url: jdbc:mysql://localhost:3306/other_data?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driverClassName: com.mysql.jdbc.Driver

  

四、编写代码

  结构如下:

  

  1、枚举类DataSourceName:

  该类用来存放数据源的名称,定义两个数据源名称分别为PRIMARYSECOND

package com.example.demo.enums;

/**
* DataSource的name常量
* 便于切换
* @author 我命倾尘
*/
public enum DataSourceName { /**
* 主数据源 spring_boot_demo
*/
PRIMARY("PRIMARY"), /**
* 副数据源other_data
*/
SECOND("SECOND"); private String dataSourceName;
private DataSourceName(String dataSourceName){
this.dataSourceName=dataSourceName;
}
DataSourceName(){ }
public String getDataSourceName(){
return this.dataSourceName;
}
}

  2、配置类DynamicDataSourceConfig:

  通过@ConfigurationProperties读取配置文件中的数据源配置信息,并通过DruidDataSourceBuilder.create().build()创建数据连接,将多个数据源放入map,注入到IoC中:

package com.example.demo.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.example.demo.bean.DynamicDataSource;
import com.example.demo.enums.DataSourceName;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map; /**
* @author 我命倾尘
*/
@Configuration
public class DynamicDataSourceConfig {
/**
* 创建DataSource Bean,将数据源配置从配置文件中读出
*/ @Bean
@ConfigurationProperties("spring.datasource.druid.primary")
public DataSource oneDataSource(){
return DruidDataSourceBuilder.create().build();
} @Bean
@ConfigurationProperties("spring.datasource.druid.second")
public DataSource twoDataSource(){
return DruidDataSourceBuilder.create().build();
} /**
* 将数据源放入到 这个map中,注入到IoC
*/
@Bean
@Primary
public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource){
Map<Object,Object> targetDataSources=new HashMap<>(2);
targetDataSources.put(DataSourceName.PRIMARY.getDataSourceName(),oneDataSource);
targetDataSources.put(DataSourceName.SECOND.getDataSourceName(),twoDataSource);
return new DynamicDataSource(oneDataSource,targetDataSources);
}
}

  3、动态数据源DynamicDataSource:

  通过继承AbstractRoutingDataSource类,在构造函数中调用父类的方法,将配置类中放入map的数据源集合定为备选数据源,将传来的oneDataSource作为默认数据源

package com.example.demo.bean;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map; /**
* @author 我命倾尘
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder=new ThreadLocal<>();
/**
* 配置DataSource
* 设置defaultTargetDataSource为主数据库
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,Object> targetDataSources){
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
public static String getDataSource(){
return contextHolder.get();
}
public static void setDataSource(String dataSource){
contextHolder.set(dataSource);
}
public static void clearDataSource(){
contextHolder.remove();
} @Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
}

  setTargetDataSources设置备选的数据源集合,

  setDefaultTargetDataSource设置默认数据源,

  determineCurrentLookupKey决定当前数据源的对应的key。

  4、自定义注释类DataSource:

package com.example.demo.annotation;

import com.example.demo.enums.DataSourceName;

import java.lang.annotation.*;

/**
* @author 我命倾尘
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceName value() default DataSourceName.PRIMARY;
}

  @Documented指定被标注的注解会包含在javadoc中,

  @Target指定注释可能出现在Java程序中的语法位置(ElementType.METHOD则说明注解可能出现在方法上),

  @Retention指定注释的保留时间(RetentionPolicy.RUNTIME则是在java文件编译成class类时也依旧保存该注释)。

  5、切面类DataSourceAspect:

package com.example.demo.aspect;

import com.example.demo.annotation.DataSource;
import com.example.demo.bean.DynamicDataSource;
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.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* @author 我命倾尘
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
private Logger log= LoggerFactory.getLogger(DataSourceAspect.class); /**
* 切点:所有配置DataSource注解的方法
*/
@Pointcut("@annotation(com.example.demo.annotation.DataSource)")
public void dataSourcePointCut(){ } @Around(value = "dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
Object result;
MethodSignature signature=(MethodSignature)point.getSignature();
Method method=signature.getMethod();
DataSource ds=method.getAnnotation(DataSource.class);
/**
* 判断DataSource的值
* 获取当前方法应用的数据源
*/
DynamicDataSource.setDataSource(ds.value().getDataSourceName());
try{
result=point.proceed();
}finally {
DynamicDataSource.clearDataSource();
}
return result;
} @Override
public int getOrder() {
return 1;
}
}

  Spring框架有很多相同接口的实现类,提供了Ordered接口来处理相同接口实现类之间的优先级问题。

  通过环绕切面,对方法上的注释进行了检验,如果获取到有DataSource注释,则会进行数据源的切换,否则按默认数据源进行处理。

  6、引入配置类:

  既然手动配置了动态切换数据连接池,就要在入口类中排除自动引入,并引入数据源的配置类,以及开启AOP:

package com.example.demo;

import com.example.demo.config.DynamicDataSourceConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import; @MapperScan("com.example.demo.mapper")
@Import({DynamicDataSourceConfig.class})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAspectJAutoProxy
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

  通过@Import引入配置类,在@SpringBootApplication后进行自动引入的排除。

  @EnableAspectJAutoProxy用来开启AOP。

五、简单测试

  1、不使用注解:

  UserController中的方法如下:

  @RequestMapping("/user/age")
  public int getAgeOfUser(){
return userService.getAgeByUsername("springbootdemo");
  }

  所得到的结果如下:

  

  这个结果是从主数据源spring_boot_demo数据库的表中得到的数据。

  2、在方法前添加注解@DataSource(DataSourceName.SECOND):

  UserController中的方法如下:

  @RequestMapping("/user/age")
  @DataSource(DataSourceName.SECOND)
  public int getAgeOfUser(){
return userService.getAgeByUsername("springbootdemo");
  }

  结果如下:

  

  这个结果则是从辅数据源other_data中得到的数据。

  前言:SpringBoot框架:使用mybatis连接mysql数据库完成数据访问(二)

SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)的更多相关文章

  1. SSM框架、Druid连接池实现多数据源配置(已上线使用)

    总体大概流程: 1.配置数据源.账密(账密一致,文章不多阐述) driverClassName = com.mysql.jdbc.Driver   validationQuery = SELECT 1 ...

  2. 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...

  3. AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/u012881904/article/de ...

  4. Java注解--实现动态数据源切换

    当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换. 实现原理 在Spring 2.0.1中引入了Abstract ...

  5. springboot整合druid连接池、mybatis实现多数据源动态切换

    demo环境: JDK 1.8 ,Spring boot 1.5.14 一 整合durid 1.添加druid连接池maven依赖 <dependency> <groupId> ...

  6. springboot集成druid连接池

    使用druid连接池主要有几步: 1.添加jar和依赖 <groupId>org.mybatis.spring.boot</groupId> <artifactId> ...

  7. SpringBoot学习笔记:动态数据源切换

    SpringBoot学习笔记:动态数据源切换 数据源 Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法.通常,DataSource使用URL和一些凭据来建立数据 ...

  8. Druid连接池和springJDbc框架-Java(新手)

    Druid连接池: Druid 由阿里提供 安装步骤: 导包 durid1.0.9 jar包 定义配置文件 properties文件 名字任意位置也任意 加载文件 获得数据库连接池对象 通过Durid ...

  9. 用AOP拦截自定义注解并获取注解属性与上下文参数(基于Springboot框架)

    目录 自定义注解 定义切面 获取上下文信息JoinPoint ProceedingJoinPoint 定义测试方法 测试结果 小结 AOP可以用于日志的设计,这样话就少不了要获取上下文的信息,博主在设 ...

随机推荐

  1. 5.SSH 免密码登陆

    SSH 免密码登陆 serverA 机器上 userA 用户,想要免密钥登陆到serverB机器上 1.集群中的所有机器 安装ssh 服务端sudo apt-get updatesudo apt-ge ...

  2. Istio 网络弹性 实践 之 故障注入 和 调用超时

    网络弹性介绍 网络弹性也称为运维弹性,是指网络在遇到灾难事件时快速恢复和继续运行的能力.灾难事件的范畴很广泛,比如长时间停电.网络设备故障.恶意入侵等. 超时时间 工作中常常会碰到这样的开发.测试场景 ...

  3. 第4篇 Scrum 冲刺博客

    1.站立会议 照骗 进度 成员 昨日完成任务 今日计划任务 遇到的困难 钟智锋 重新设计项目执行流程 实现技能 庄诗楷 游戏窗口的制作 制作了开始游戏的界面,感觉还不错 无 易德康 完成所有棋子的移动 ...

  4. python官网打不开

    这可能是因为该站点使用过期的或不安全的 TLS 安全设置. 解决:依次打开IE的Internet选项.高级,往下拉,找到安全模块,勾上四个使用:使用SSL 3.0.使用TLS 1.0.使用TLS 1. ...

  5. mybatis批量添加数据的三种方式

    原文地址:https://www.cnblogs.com/gxyandwmm/p/9565002.html

  6. 力扣Leetcode 461. 汉明距离

    给你一个数组 arr ,请你将每个元素用它右边最大的元素替换,如果是最后一个元素,用 -1 替换. 完成所有替换操作后,请你返回这个数组. 示例: 输入:arr = [17,18,5,4,6,1] 输 ...

  7. JS - 日期时间比较函数

    JS日期比较(yyyy-mm-dd) function duibi(a, b) { var arr = a.split("-"); var starttime = new Date ...

  8. git 合并两个分支的某个文件

    软件开发基本都是多个feature分支并行开发,而在上线前有可能某个分支的开发或测试还没有完成,又或者是产品调整,取消了该分支功能的上线计划,我们在release前不合并该分支即可,然而如果该分支中的 ...

  9. 遇到doxygen生成的chm文档目录如果有中文是乱码?

    原因不在于doxygen,它没有问题,问题出在微软的HTML Help Workshop的hhc.exe不支持utf8.所以要解决这个问题,需要做两个额外的步骤: 1.将html/index.hhp中 ...

  10. 记录使用vs code两天的心得

    一个字 就是骚~感觉以后写博客都省了