一、引入依赖

  引入数据库连接池的依赖——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. java如何将char类型的数字转换成int型的数字,而不是Ascii

    如何把 char ‘3’ 转为 int 3, 大家应该知道,不能直接转化,那样得到是‘3’的Ascii. 如下面: public class Leet { public static void mai ...

  2. Nordic52840SDK学习之定时器

    Nordic 52840 SDK学习之定时器 今天开始学习52840SDK,特在此处记录学习内容,防止以后忘记,或许可以给以后的初学者提供一些帮助.如有错误,请发邮件至843036544@qq.com ...

  3. [WPF][Rubyer] 写一个自己的 UI 库 (二) - Icon

    前言 制作 WPF 的图标包,主要介绍从 iconfont 下载的图标包导入到 WPF 使用: 1. 添加文件 Ruyber 下添加 自定义控件(WPF) Icon.cs.类 IconType.cs. ...

  4. Robot Framework(7)——接口测试

    一.准备工作 1.安装requests工具(2.22.0) 下载地址:https://pypi.org/project/requests/ 安装方式: 1>下载压缩文件,解压,目录切到解压目录, ...

  5. 状态压缩动态规划(状压DP)详解

    0 引子 不要999,也不要888,只要288,只要288,状压DP带回家.你买不了上当,买不了欺骗.它可以当搜索,也可以卡常数,还可以装B,方式多样,随心搭配,自由多变,一定符合你的口味! 在计算机 ...

  6. VS Code安装yo(Yeoman) 插件下载.net core 模版代码开发

    在安装插件以前,请看插件地址的相关依赖 Pre-requirements [Node.js] (https://nodejs.org) [npm] (https://www.npmjs.com) [Y ...

  7. springboot-swagger配置

    原文地址 https://www.cnblogs.com/softidea/p/6251249.html https://www.cnblogs.com/xiebq/p/9181517.html 1p ...

  8. 焦大:seo该研究用户需求还是搜索算法

    http://www.wocaoseo.com/thread-62-1-1.html 上一篇博客我写了用户需求点是做seo排名最首要关注的东西,其实这个我在以前也一直说的,seo有两大核心,一个是检索 ...

  9. 日志记录——logging模块

    Logging:日志记录是为了跟踪记录软件运行时,发生的事件,包括出错,提示信息等等.log日志级别:日志级别大小关系为:CRITICAL > ERROR > WARNING > I ...

  10. C#操作Excel开发报表系列整理

    C#操作Excel进行报表开发系列共写了八篇,也已经有很久没有新东西了,现在整理一下,方便以后查阅,如果有写新的,会同时更新.需要注意的是因为Office的版本不同,实际的代码可能会有所不同,但是都是 ...