关于AbstractRoutingDataSource动态切换数据源是我在研究某开源项目时候才注意到的,大概就看懂了Spring AOP切面这里,根据作者的意思是通过继承这个抽象类可以实现数据源的动态切换,也就是Controller调用Service的时候会切换数据源。最终研究了一天也没发现什么结果,第二天便尝试查看源码和查看相关资料,试图揭开这个疑惑


  • 首先贴上源代码如下:
package com.zdd.data.aspect;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** 获取数据源* */
public class ChooseDataSource extends AbstractRoutingDataSource {public static Map<String, List<String>> METHODTYPE = new HashMap<String, List<String>>();// 获取数据源名称protected Object determineCurrentLookupKey() {System.out.println(HandleDataSource.getDataSource());return HandleDataSource.getDataSource();}// 设置方法名前缀对应的数据源public void setMethodType(Map<String, String> map) {for (String key : map.keySet()) {List<String> v = new ArrayList<String>();String[] types = map.get(key).split(",");for (String type : types) {if (StringUtils.isNotBlank(type)) {v.add(type);}}METHODTYPE.put(key, v);}}
}

package com.zdd.data.aspect;import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
public class DataSourceAspect {private final org.slf4j.Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);@Pointcut("execution(* com.zdd.data.service.impl.*.*(..))")public void aspect() {}/*** 配置前置通知,使用在方法aspect()上注册的切入点*/@Before("aspect()")public void before(JoinPoint point) {String className = point.getTarget().getClass().getName();String method = point.getSignature().getName();logger.info(className + "." + method + "(" + StringUtils.join(point.getArgs(), ",") + ")");try {L: for (String key : ChooseDataSource.METHODTYPE.keySet()) {for (String type : ChooseDataSource.METHODTYPE.get(key)) {if (method.startsWith(type)) {System.out.println(key);HandleDataSource.putDataSource(key);break L;}}}} catch (Exception e) {logger.error(e.toString());HandleDataSource.putDataSource("write");}}@After("aspect()")public void after(JoinPoint point) {HandleDataSource.clear();}
}

package com.zdd.data.aspect;/***/
public class HandleDataSource {// 数据源名称线程池private static final ThreadLocal<String> holder = new ThreadLocal<String>();public static void putDataSource(String datasource) {holder.set(datasource);}public static String getDataSource() {return holder.get();}public static void clear() {holder.remove();}
}

  #以上是核心代码,我疑惑的地方在于AbstractRoutingDataSource这里,刚开始去看的时候不明白为什么继承这个就能实现数据源切换,最后进入AbstractRoutingDataSource.class去查看究竟,发现继承了一个AbstractDataSource的抽象类,这里我突然有点明白AbstractRoutingDataSource的意思,一个路由数据源,一般用到Routing的地方都可以进行动态配置。

  • 大致翻译一下源码作者对这个类的说明:调用一个基于各种目标数据源。

继续往下看又看到几个重要信息

上图发现2个方法分别是setTargetDataSources(注入目标数据源) 和setDefaultTargetDataSource(注入默认的数据源);然后想到是否可以通过Spring 配置来注入这2个方法,带着这个思绪继续接着继续往下看;然后又看到比较重要的信息如下:

上图发现有getConnection方法,jdbc获取连接的方法,而当我点进determineTargetDataSource方法时得到了一个确切答案,代码如下:

  上图大致的意思是会调用determineCurrentLookupKey这个抽象方法获取数据源,如果是null则获取默认的数据源,反之则是获取我们注入的数据源, 这样就实现了数据源的动态切换, 这时候我通过断点启动也验证了这一个流程确实如此。


分析了一个动态切换流程后,那么问题来了,如何让我们的代码实现这个功能呢?其实就是重写AbstractRoutingDataSource方法后再通过aop动态切换数据源,那么如何知道切换条件是什么?这时候可以根据Service层的方法来确定,一般我们Service都是会以find,add,delete,update 开头; 例如发现是find,get开头则走读库,delete,update则走写库。

#分析完后把spring配置文件贴上来

<bean id="dataSource" class="com.zdd.data.aspect.ChooseDataSource" lazy-init="true"><description>数据源</description><property name="targetDataSources"><map key-type="java.lang.String" value-type="javax.sql.DataSource"><!-- write --><entry key="write" value-ref="writedataSource" /><!-- read --><entry key="read" value-ref="readdataSource" /></map></property><!-- 设置默认的目标数据源为读 --><property name="defaultTargetDataSource" ref="readdataSource" /><property name="methodType"><map key-type="java.lang.String"><!-- read --><entry key="read" value=",get,,find,select,count,list,query," /><!-- write --><entry key="write" value=",add,insert,create,update,delete,remove," /></map></property></bean>

#上面的配置大概流程是先指定2个数据源注入targetDataSources,然后配置一组数组用来存放判断走读库还是写库的条件,如果是read那么肯定是把read注入的数据源拿出来,如果是write则把write的数据源取出来。

更新时间-----------------------------------------------2018年5月16日 13:54:27------------------------------------------------------------

#但是这时候会有一个奇怪的现象就是每次调用的时候会走chooseDataSource再走AOP,经查询发现是事务问题导致,因为它先走了事务切面,然后事务还没结束的时候如果再去切换数据源的话是不成立的。方法解决很简单,设置一下切换数据源的AOP的优先级,确保在事务执行之前就已经切换数据源,如下图:

转载于:https://www.cnblogs.com/zdd-java/p/zdd_datasource_aop.html

【原】继承AbstractRoutingDataSource再通过AOP实现动态数据源切换相关推荐

  1. SpringBoot多数据源切换,AOP实现动态数据源切换

    SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFact ...

  2. Spring AOP之四:利用AOP实现动态数据源切换

    2019独角兽企业重金招聘Python工程师标准>>> 简介和依赖 项目的前提是安装了MySQL数据库,并且建立了2个数据库一个是master,一个是slave,并且这2个数据库都有 ...

  3. 动态数据源切换--AbstractRoutingDataSource

    转载自http://blog.csdn.net/x2145637/article/details/52461198 在Spring 2.0.1中引入了AbstractRoutingDataSource ...

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

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

  5. springboot动态数据源切换(多数据源配置)

    动态数据源切换即多数据源切换,由于业务的需要或者历史的遗留等原因,一个项目中配置了多个数据库,用于查询不同类型的数据,因此我们就需要经常在各个库中切换数据源,接下来我们将进行具体的说明: 项目结构如下 ...

  6. spring environment_程序员:Spring项目中简单几步实现多个动态数据源切换

    每一个请求与其他的用户是面对不同的数据库,这就需要用到动态数据源切换,来满足不同数据库.不同数据表(不同数据源)的灵活调用. 动态数据源切换 满足mysql.oracle等主流数据库进行动态数据源切换 ...

  7. Spring(AbstractRoutingDataSource)实现动态数据源切换

    参考:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中 ...

  8. Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

    原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...

  9. spring boot使用AbstractRoutingDataSource实现动态数据源切换

    一.AbstractRoutingDataSource Spring boot提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前, ...

最新文章

  1. mysql数字转换中文版_MYSQL字符数字转换
  2. 用Go语言建立一个简单的区块链part5:地址
  3. 在product search result里点击某个product出现白屏问题
  4. 100行代码实现了多线程,批量写入,文件分块的日志方法
  5. sun.misc.Unsafe的理解
  6. 51单片机培训计划(新订)
  7. 测试用户名字 陈一王二张三李四钱五赵六钱七张八周九吴十
  8. 车载网络: 常见车载网络
  9. 未来公园怎么能少了广场舞?
  10. STM32F1xx_StdPeriph_Driver——SPI
  11. Android学习网站资源
  12. You are using pip version 8.1.2, however version 21.3.1 is available pip安装docker-compose版本问题解决
  13. SAP中通过定制开发在寄售结算MRKO中实现发票校验功能
  14. ignore在mysql中什么意思_INSERTIGNORE与INSERTINTO的区别_MySQL
  15. java方法声明无效_Java错误 - “无效的方法声明;需要返回类型”
  16. python经典类新式类_菜鸟世界 -python进阶---新式类与经典类
  17. Python之520爱的表白
  18. c libxml2解析html,在Python中,lxml和libxml2哪个更适合解析格式错误的html?
  19. 聊聊常见的服务(接口)认证授权
  20. 解决:运行pytest时,报错:'TestCaseFunction' object has no attribute 'get_marker'

热门文章

  1. E: 无法获得锁 /var/lib/dpkg/lock - open (11: 资源暂时不可用)
  2. 文秘专业计算机基础考题,《计算机应用基础》课程无纸化试题库建设及应用分析...
  3. 删除有序数组中的重复项 IIPython解法
  4. json添加元素 vue_详解通过JSON数据使用VUE.JS
  5. canvas 元素绑定事件_绘制SVG内容到Canvas的HTML5应用
  6. MySQL中清空表和截断表的区别(新手入门.)
  7. 我的世界java版tis,我的世界1.7.10~1.8.8 9tis-3d回到汇编的时代mod
  8. cat3 utp是不是网线_UTPCAT3网络双绞线规格型号
  9. android中止线程_Android如何结束并重启线程? | 学步园
  10. redis源码分析 ppt_Redis源码分析之客户端+数据库