springboot 多数据源 读写分离 AOP方式

大家好,我是烤鸭:

        今天分享springboot读写分离配置。

         环境:

                 springboot  2.1.0.RELEASE

         场景说明,目前的需求是 读数据源 * 2 + 写数据源 * 1

 

1.    配置文件


    application.yml

server:port: 8085
spring:application:name: test-data-testdatasource:write:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test.Devdriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource   connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100readaw:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test!idriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource   connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100readdc:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test!idriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource   connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100
#mybatis
mybatis:###把xml文件放在com.XX.mapper.*中可能会出现找到的问题,这里把他放在resource下的mapper中mapper-mapperLocations: classpath*:mapper/**/**/*.xmltype-aliases-package: com.test.test.pojoconfiguration:map-underscore-to-camel-case: truecache-enabled: falsecall-setters-on-nulls: trueuseGeneratedKeys: true

2.    配置类


 DataSourceConfig.java

 默认 读数据源,如果需要增加或者减少数据源需要修改 myRoutingDataSource 方法中的参数

package com.test.test.config.db;import com.test.test.datasource.MyRoutingDataSource;
import com.test.test.datasource.enums.DBTypeEnum;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》* 79. Data Access* 79.1 Configure a Custom DbSource* 79.2 Configure Two DataSources*/@Configuration
public class DataSourceConfig {@AutowiredEnvironment environment;@Bean@ConfigurationProperties("spring.datasource.readaw")public DataSource readDataSourceAw() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"readaw");return hikariDataSource;}@Bean@ConfigurationProperties("spring.datasource.readdc")public DataSource readDataSourceDc() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"readdc");return hikariDataSource;}@Bean@ConfigurationProperties("spring.datasource.write")public DataSource writeDataSource() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"write");return hikariDataSource;}@Beanpublic DataSource myRoutingDataSource(@Qualifier("readDataSourceAw") DataSource readDataSourceAw,@Qualifier("readDataSourceDc") DataSource readDataSourceDc,@Qualifier("writeDataSource") DataSource writeDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DBTypeEnum.READ_AW, readDataSourceAw);targetDataSources.put(DBTypeEnum.READ_DC, readDataSourceDc);targetDataSources.put(DBTypeEnum.WRITE, writeDataSource);MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();myRoutingDataSource.setDefaultTargetDataSource(readDataSourceAw);myRoutingDataSource.setTargetDataSources(targetDataSources);return myRoutingDataSource;}public HikariDataSource buildDataSource(DataSource dataSource,String dataSourcePrefix){HikariDataSource hikariDataSource= (HikariDataSource) dataSource;hikariDataSource.setDriverClassName(environment.getProperty("spring.datasource."+dataSourcePrefix+".driver-class-name"));hikariDataSource.setJdbcUrl(environment.getProperty("spring.datasource."+dataSourcePrefix+".jdbc-url"));hikariDataSource.setUsername(environment.getProperty("spring.datasource."+dataSourcePrefix+".username"));hikariDataSource.setPassword(environment.getProperty("spring.datasource."+dataSourcePrefix+".password"));hikariDataSource.setMinimumIdle(Integer.parseInt(environment.getProperty("spring.datasource."+dataSourcePrefix+".minIdle")));hikariDataSource.setConnectionTimeout(Long.parseLong(environment.getProperty("spring.datasource."+dataSourcePrefix+".connectionTimeout")));hikariDataSource.setValidationTimeout(Long.parseLong(environment.getProperty("spring.datasource."+dataSourcePrefix+".validationTimeout")));hikariDataSource.setMaximumPoolSize(Integer.parseInt(environment.getProperty("spring.datasource."+dataSourcePrefix+".maxPoolSize")));return hikariDataSource;}
}

MyBatisConfig.java

注意映射mapper文件路径是在这里修改的,因为重新注入了sqlSession, yml中配置的无效

package com.test.test.config.mybatis;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.annotation.Resource;
import javax.sql.DataSource;@EnableTransactionManagement
@Configuration
public class MyBatisConfig {@Resource(name = "myRoutingDataSource")private DataSource myRoutingDataSource;@Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(myRoutingDataSource);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));return sqlSessionFactoryBean.getObject();}@Beanpublic PlatformTransactionManager platformTransactionManager() {return new DataSourceTransactionManager(myRoutingDataSource);}
}

DataSourceAop.java

aop配置类,通过aop的方式限制哪个service的方法连接哪个数据源
目前是根据类上的注解来判断,可以修改为根据方法的注解来判断走哪个数据源

package com.test.test.datasource.aop;import com.test.test.datasource.annotation.DbSource;
import com.test.test.datasource.handler.DBContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Component
public class DataSourceAop {/*** 另一种写法:if...else...  判断哪些需要读从数据库,其余的走主数据库*/@Before("execution(* com.test.test.service.impl.*.*(..))")public void before(JoinPoint jp){MethodSignature methodSignature = (MethodSignature) jp.getSignature();Method method = methodSignature.getMethod();System.out.println("拦截到了" + jp.getSignature().getName() +"方法...");Class<?> targetClass = jp.getTarget().getClass();boolean flag = targetClass.isAnnotationPresent(DbSource.class);//包含数据源注解,数据源为注解中的类if(flag){//获取注解的valueDbSource annotation = targetClass.getAnnotation(DbSource.class);String value = annotation.value();DBContextHolder.read(value);}else {//不包含注解,查询方法默认走 默认读数据源if (StringUtils.startsWithAny(method.getName(), "get", "select", "find")) {DBContextHolder.read("");}else {DBContextHolder.write();}}}
}

DBTypeEnum.java

数据源枚举,增加和减少数据源修改即可

public enum DBTypeEnum {READ_AW, READ_DC, WRITE;}

DBContextHolder.java

数据源切换类,保持当前线程绑定哪个数据源

package com.test.test.datasource.handler;import com.test.test.datasource.enums.DBTypeEnum;
import org.apache.commons.lang3.StringUtils;import java.util.concurrent.atomic.AtomicInteger;
/*** @Author gmwang* @Description // 数据源切换类* @Date 2019/4/30 9:20* @Param* @return**/
public class DBContextHolder {private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();private static final AtomicInteger counter = new AtomicInteger(-1);public static void set(DBTypeEnum dbType) {contextHolder.set(dbType);}public static DBTypeEnum get() {return contextHolder.get();}public static void read(String value) {if(StringUtils.isBlank(value)){set(DBTypeEnum.READ_AW);System.out.println("切换到读"+DBTypeEnum.READ_AW.toString());}if (DBTypeEnum.READ_DC.toString().equals(value)){set(DBTypeEnum.READ_DC);System.out.println("切换到读"+DBTypeEnum.READ_DC.toString());}}public static void write() {set(DBTypeEnum.WRITE);System.out.println("切换到写"+DBTypeEnum.WRITE.toString());}
}

MyRoutingDataSource.java

多数据源的路由类

package com.test.test.datasource;import com.test.test.datasource.handler.DBContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
/*** @Author gmwang* @Description //多数据源的路由* @Date 2019/4/30 9:38* @Param* @return**/
public class MyRoutingDataSource extends AbstractRoutingDataSource {/*** @Author gmwang* @Description //根据Key获取数据源的信息,上层抽象函数的钩子* @Date 2019/4/30 9:39* @Param []* @return java.lang.Object**/@Nullable@Overrideprotected Object determineCurrentLookupKey() {return DBContextHolder.get();}
}

DbSource

数据源注解,加在serivice实现类上,指定 value,AOP根据注解获取指定的数据源。


package com.test.test.datasource.annotation;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DbSource {String value();
}

例如本例中,默认读库是  READ_AW ,如果不加注解默认,读取默认库。如果指定注解 READ_DC,就用指定的数据源。

 

3.    结果测试

伪代码:

 

在tes方法中使用查询(不同的库)后插入操作,结果如图所示。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412809.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

201771010137 赵栋 《第十二周学习总结》

一&#xff1a;理论部分 1.&#xff08;1&#xff09; 用户界面(User Interface)用户与计算机系统(各种程序)交互的接口 &#xff08;2&#xff09;图形用户界面(Graphical User Interface)以图形方式呈现的用户界面 2.AWT:Java 的抽象窗口工具箱&#xff08; Abstract WindowT…

多服务器 elk 搭建 [elasticsearch 7.0 ]

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下多服务器的elk搭建。 1. 流程规划 2. 执行搭建 最新的版本和对应下载地址可以在官网查询到 https://www.elastic.co/cn/products/ 2.1 elasticsearch 7.0 搭建 2.1.1 下载 wget https://artifacts.elastic.co/…

知乎问答:现在程序员的工资是不是被高估了?

对于优秀的程序员来说&#xff0c;薪酬的天花板犹如发际线&#xff0c;没有最高只有更高。而对于只想「混日子」的程序员来说&#xff0c;高薪很可能是泡沫&#xff0c;风一吹就碎。 一、程序员的工资真的高吗&#xff1f; 《2018年中国程序员生存状况报告》&#xff0c;来源&a…

lombok 的bug?lombok 导致 springmvc 使用 @RequestBody注解 接收 json数据 对象参数绑定失败

大家好&#xff0c;我是烤鸭&#xff1a; lombok 导致 springmvc 使用 RequestBody注解 接收 json数据 对象参数绑定失败。 环境版本&#xff1a; spring 5.x 1. 场景复现 问题出现在创建对象的属性名称。比如我有一个类中的属性值是 String aTest; 首字…

web APIS

WEB API系列&#xff1a; 很多人都很迷惑&#xff0c;既然有了WCF为什么还要有WEB API&#xff1f;WEB API会不会取代WCF&#xff1f; 就我的看法&#xff0c;WCF提供的是一种RPC实现的集合&#xff0c;WCF的设计更多地考虑了SOA的场景&#xff0c;以及各种RPC的问题。很多人也…

dubbo 整合 zipkin,最简单的方式,亲测有效

大家好&#xff0c;我是烤鸭。 之前也试过网上很多版本&#xff0c;看了好多文章。现在分享最简单的方式&#xff0c;代码侵入性最小的。 1. 修改pom,引入jar。 <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency><…

[Network Architecture]DPN(Dual Path Network)算法详解(转)

https://blog.csdn.net/u014380165/article/details/75676216 论文&#xff1a;Dual Path Networks 论文链接&#xff1a;https://arxiv.org/abs/1707.01629 代码&#xff1a;https://github.com/cypw/DPNs MXNet框架下可训练模型的DPN代码&#xff1a;https://github.com/m…

javax.script.ScriptException: ReferenceError: xxx is not defined in eval

大家好&#xff0c;我是烤鸭&#xff1a; 今天使用 javax.script.ScriptEngine 遇到一个奇怪的问题&#xff0c;无法识别js方法。 1. 报错内容&#xff1a; javax.script.ScriptException: ReferenceError: "a" is not defined in <eval> at line number…

Python的特殊成员

Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用’from module import *’导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 核心风格&#xff1a;避免用下划线作为变量名的开始。 现在我们来总结下所有的系统定义属性和方法&#xff0c; 先来看下保留属性&#…

java 实现 常见排序算法(四)基数排序

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下基础排序算法之基数排序。 1. 基数排序&#xff1a; 原理&#xff1a;基数排序&#xff08;radix sort&#xff09;属于“分配式排序”&#xff08;distribution sort&#xff09;&#xff0c;又称“桶子法”&#…

今天分享一下做支付宝小程序遇到的坑。ISV权限不足,建议在开发者中心检查对应功能是否已经添加。验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下做支付宝小程序遇到的坑。pom版本 <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-j…

Will not attempt to authenticate using SASL | dubbo项目启动特别慢,拉取 zookeeper 服务日志打印特别慢

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下使用dubbo遇到的几个问题。 1. cause: KeeperErrorCode ConnectionLoss for /dubbo/ xxx 异常如下&#xff1a; pid9965&qos.accept.foreign.ipfalse&qos.enabletrue&qos.port10887&timestamp1567…

redis集群搭建【简版】

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下redis集群安装&#xff0c;写的比较简单&#xff0c;就是做个记录。 1. 下载&安装 wget http://download.redis.io/releases/redis-5.0.4.tar.gz tar -zxvf redis-5.0.4.tar.gz 解压并编译 https://redis.io/ 2.…

x-shell 通过堡垒机连接 ssh多个机器 自动化脚本

大家好&#xff0c;我是烤鸭&#xff1a; 1. 连接vpn 这一步就没什么说的了。我们用的easyconnect。登录后弹出堡垒机页面。 easyconnect 官网&#xff1a;https://sslvpn.zjweu.edu.cn/com/installClient.html 2. 登录堡垒机账号 登录堡垒机之后&#xff0c;如图&#xff…

javax.mail.MessagingException: while sending message;Connection reset by peer: socket write error

大家好&#xff0c;我是烤鸭&#xff1a; 阿里云邮件推送服务报错。当你也使用阿里云的邮件推送服务提示上面这个错误的话&#xff0c;我先告诉你原因和目前能想到的解决方案。 解决思路&#xff1a; 1. 换企业邮箱&#xff0c;阿里的上限15M&#xff0c;网易的不知道&…

李晓菁201771010114《面向对象程序设计(java)》第十三周学习总结

理论知识&#xff1a;事件处理 1.事件源&#xff1a;能够产生事件的对象都可以成为事件源&#xff0c;如文本框&#xff0c;按钮等。一个事件源是一个能够注册监听器并向监听器发送事件对象的对象。 2.事件监听器&#xff1a;事件监听器对象接收事件源发送的通告&#xff08;事…

记一次 OOM 的原因和处理 出现大量 close_wait,项目无法访问 activeMq和 poi 出现的 OOM

大家好&#xff0c;我是烤鸭: 记一次项目无法访问的事件和处理。由于某个模块每隔几天就会出现无法访问&#xff0c;目前的最简单粗暴的方法就是重启。 1. 现象 项目内日志打印正常&#xff0c;经过dubbo的rpc服务和接口调用正常。http接口无法访问。提示nginx 502。 2.…

谷歌浏览器中安装JsonView扩展程序

实际开发工作中经常用到json数据&#xff0c;那么就会有这样一个需求&#xff1a;在谷歌浏览器中访问URL地址返回的json数据能否按照json格式展现出来。 比如&#xff0c;在谷歌浏览器中访问&#xff1a;http://jsonview.com/example.json 展现效果如下&#xff1a; 那么安装了…

Serialized class com.xxx.xxxService must implement java.io.Serializable

大家好&#xff0c;我是烤鸭&#xff1a; 使用dubbo的时候&#xff0c;遇到如下的问题。 Serialized class com.xxx.xxxService must implement java.io.Serializable 1. 异常 dubbo无论使用哪个协议传递参数的时候&#xff0c;都需要参数实现序列化接口。 所以提示这个…

CS229 7.1应用机器学习中的一些技巧

本文所讲述的是怎么样去在实践中更好的应用机器学习算法&#xff0c;比如如下经验风险最小化问题&#xff1a; 当求解最优的 后&#xff0c;发现他的预测误差非常之大&#xff0c;接下来如何处理来使得当前的误差尽可能的小呢&#xff1f;这里给出以下几个选项&#xff0c;下面…