一、主要依赖

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/>
</parent> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

二、yml

# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://127.0.0.1:3306/master?characterEncoding=UTF-8
username: root
password: root
#树熊数据源
slave:
enabled : true
url: jdbc:mysql:////127.0.0.1:3306/slave?characterEncoding=UTF-8
username: root
password: root # 初始连接数
initial-size: 10
# 最大连接池数量
max-active: 100
# 最小连接池数量
min-idle: 10
# 配置获取连接等待超时的时间
max-wait: 60000
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true

三、实现

3.1、@DataSource和DataSourceType

/**
* 数据源
* @author DUCHONG
*/
public enum DataSourceType
{
/**
* 主库
*/
MASTER, /**
* 从库
*/
SLAVE
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 自定义多数据源切换注解
*
* @author DUCHONG
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource
{
/**
* 切换数据源名称
*/
public DataSourceType value() default DataSourceType.MASTER;
}

3.2、DynamicDataSourceContextHolder

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 数据源切换处理
*
* @author DUCHONG
*/
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); /**
* 设置数据源的变量
*/
public static void setDateSourceType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
} /**
* 获得数据源的变量
*/
public static String getDateSourceType()
{
return CONTEXT_HOLDER.get();
} /**
* 清空数据源变量
*/
public static void clearDateSourceType()
{
CONTEXT_HOLDER.remove();
}
}

3.3、继承AbstractRoutingDataSource

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

import javax.sql.DataSource;
import java.util.Map; /**
* 动态数据源
*
* @author DUCHONG
*/
public class DynamicDataSource extends AbstractRoutingDataSource
{
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
} @Override
protected Object determineCurrentLookupKey()
{
return DynamicDataSourceContextHolder.getDateSourceType();
}
}

3.4、定义切面

import com.starfast.admin.common.annotation.DataSource;
import com.starfast.admin.datasource.DynamicDataSourceContextHolder;
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.annotation.Order;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* 多数据源处理
* @author DUCHONG
*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{
protected Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(com.duchong.common.annotation.DataSource)")
public void dsPointCut()
{ } @Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable
{
MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); if (null!=dataSource)
{
DynamicDataSourceContextHolder.setDateSourceType(dataSource.value().name());
} try
{
return point.proceed();
}
finally
{
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDateSourceType();
}
}
}

3.5、@Configuration

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.starfast.admin.common.enums.DataSourceType;
import com.starfast.admin.datasource.DynamicDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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; /**
* druid 配置多数据源
*
* @author DUCHONG
*/
@Configuration
public class DruidConfig
{
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource()
{
return DruidDataSourceBuilder.create().build();
} @Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource()
{
return DruidDataSourceBuilder.create().build();
} @Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource()
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource());
targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource());
return new DynamicDataSource(masterDataSource(), targetDataSources);
}
}

3.6、使用

需要切换数据源的方法上加

@DataSource(value = DataSourceType.SLAVE)

结束。

springboot项目自定义注解实现的多数据源切换的更多相关文章

  1. Springboot+Redisson自定义注解一次解决重复提交问题(含源码)

    前言   项目中经常会出现重复提交的问题,而接口幂等性也一直以来是做任何项目都要关注的疑难点,网上可以查到非常多的方案,我归纳了几点如下:   1).数据库层面,对责任字段设置唯一索引,这是最直接有效 ...

  2. springboot aop 自定义注解方式实现完善日志记录(完整源码)

    版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...

  3. springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)

    https://www.cnblogs.com/wenjunwei/p/9639909.html https://blog.csdn.net/tyrant_800/article/details/78 ...

  4. springboot项目 @Scheduled注解 实现定时任务

    使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式: 一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实 ...

  5. SpringBoot:自定义注解实现后台接收Json参数

    0.需求 在实际的开发过程中,服务间调用一般使用Json传参的模式,SpringBoot项目无法使用@RequestParam接收Json传参 只有@RequestBody支持Json,但是每次为了一 ...

  6. springboot基于方法级别注解事务的多数据源切换问题

    springBoot多数据源配置 配置读数据源 @Component @ConfigurationProperties(prefix = "jdbc.read") @Propert ...

  7. Springboot使用自定义注解实现简单参数加密解密(注解+HandlerMethodArgumentResolver)

    前言 我黄汉三又回来了,快半年没更新博客了,这半年来的经历实属不易,疫情当头,本人实习的公司没有跟员工共患难, 直接辞掉了很多人.作为一个实习生,本人也被无情开除了.所以本人又得重新准备找工作了. 算 ...

  8. springboot使用自定义注解和反射实现一个简单的支付

    优点: 未使用if else,就算以后增加支付类型,也不用改动之前代码 只需要新写一个支付类,给添加自定义注解@Pay 首先: 定义自定义注解 Pay 定义 CMBPay ICBCPay 两种支付 根 ...

  9. springboot aop 自定义注解

    枚举类: /** * Created by tzq on 2018/5/21. */ public enum MyAnnoEnum { SELECT("select"," ...

随机推荐

  1. Oracle数据库限定特定用户 特定IP 登录

    不允许test用户在 192.168.1.3 机器上访问数据库. 两种写法. 1 CREATE OR REPLACE TRIGGER DOPR AFTER LOGON ON test.schema B ...

  2. learning java 获取键盘输入

    通过Scanner类,获取键盘的输入 var sc = new Scanner(System.in); // while (sc.hasNext()){ // System.out.println(& ...

  3. P2701 [USACO5.3]巨大的牛棚Big Barn

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  4. javaee三层架构案例--简单学生管理系统

    背景 学了jdbc.jsp等需要串起来,不然会忘记 项目环境 win10 jdk11 mysql8.0.13 jar包 c3p0-0.9.5.2 commons-dbutils-1.7 jstl mc ...

  5. cesium地下模式(地表透明)4

    这篇博客主要罗列一下解决地下模式(地表透明)的相关资源 1.Cesium的Github仓库地下模式issue 有人提了这个问题,但是cesium官方没有解决这个问题,持续跟踪一下问题说不定哪天官方就解 ...

  6. hive --metastore三种模式

    在官网上对于这几种模式的介绍如下: 按Metastore数据库位置分: 1.本地/嵌入式Metastore数据库(Derby) 2.远程Metastore数据库(其他的关系型数据库,像mysql.or ...

  7. [TJOI2019]甲苯先生和大中锋的字符串——后缀自动机+差分

    题目链接: [TJOI2019]甲苯先生和大中锋的字符串 对原串建后缀自动机并维护$parent$树上每个点的子树大小,显然子树大小为$k$的节点所代表的子串出现过$k$次,那么我们需要将$[len[ ...

  8. 小程序wx.showLoading的使用

    比如说在用户点击登录的时候,为了防止用户点击点第二次,可以加一个loading,在请求结束之后就关闭

  9. LeetCode 第 149 场周赛

    成绩 一.一年中的第几天(LeetCode-1154) 1.1 题目描述 1.2 解题思路 比较容易的一题,搞清楚平年.闰年的判定规则,就很容易做出来. 1.3 解题代码 class Solution ...

  10. jQuery之编写插件

    一.学习插件编写背景 作为一名前端人员,应该注重前端复用性及组件化,更应该考虑前端的性能优化,做到代码简洁有序,不冗余.特别是在大型团队中,如果一个团队中存在多个功能相似的组件,举个栗子,拿分页组件举 ...