我这项目的读写分离方式在使用ThreadLocal实现的读写分离在迁移后的偶发错误里提了,我不再说一次了,这次是有要求读写分离与事务部分要完全脱离配置文件,程序员折腾了很久,于是我就查了一下,由于我还是比较喜欢使用xml的方式,所以就随便。。。(过程省略吧),然而,似乎是一定要声明式的方式,所以,无奈之下就只好干了。

  首先,在之前的博客里提到过,我们的读写分离方式要求我们自己的AOP拦截器必须在事务拦截器之前执行,在配置文件的方式下很容易,在aop的配置里设置一下Order就好了。然而,Spring Boot的@EnableTransactionManagement注解中已经把这部分固定了,官方文档似乎说它是和@Transactional配合使用的,总之几乎没有留下什么插手的余地(如果大家有好办法,希望能告诉我一下):

  这个ProxyTransactionManagementConfiguration类中,就直接手new了TransactionInterceptor:

    public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}

  虽然这里可以通过上图的方式加点什么,但这个事务体系本身是非常独立的,而且在启动过程中就已经确定下来了,既然要调节它和自定义的aop的执行顺序,我想只能先统一他们的执行策略。之前自定义的aop是在启动过程中就被加入到一个拦截器的调用链中:

  由于Spring Boot很多东西并没有留什么扩展的余地(就好像前面那个new),如果完全不用配置文件,使用Spring Boot的方式,虽然没有什么顺眼的方法,其实也还是能做的,先提个建议,能不用的情况下,最好不要用。方法是自定义一个事务拦截器,抛弃@EnableTransactionManagement,测试代码如下,不要在意命名,图方便直接在原来的上面改的:

    @Bean(name = "newDataSourceAop")
public NewDataSourceAop newDataSourceAop(){
return new NewDataSourceAop();
} @Bean(name = "tInterceptor")
public TInterceptor tInterceptor(){
return new TInterceptor();
} /**
* 代理
* @return
*/
@Bean
public BeanNameAutoProxyCreator transactionAutoProxy() {
BeanNameAutoProxyCreator autoProxy = new BeanNameAutoProxyCreator();
autoProxy.setProxyTargetClass(true);// 这个属性为true时,表示被代理的是目标类本身而不是目标类的接口
autoProxy.setBeanNames("*ServiceImpl");
autoProxy.setInterceptorNames("newDataSourceAop", "tInterceptor");
return autoProxy;
}

  注意,拦截器的顺序依赖于名字字符串传入的先后顺序,@Order什么的是完全没用的,因为保存这些拦截器的是一个字符串数组。自定义的事务AOP Advice:

public class TInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

    public TInterceptor() {
setTransactionAttributes(getAttrs());
} private Properties getAttrs(){
Properties attributes = new Properties();
// 新增
attributes.setProperty("create*", "PROPAGATION_REQUIRED,ISOLATION_DEFAULT");
// 修改
attributes.setProperty("update*", "PROPAGATION_REQUIRED,ISOLATION_DEFAULT");
// 删除
attributes.setProperty("delete*", "PROPAGATION_REQUIRED,ISOLATION_DEFAULT");
//查询
attributes.setProperty("query*", "PROPAGATION_REQUIRED,ISOLATION_DEFAULT");
return attributes;
} @Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, () -> invocation.proceed());
}
}

  自定义的读写分离Advice:

@EnableConfigurationProperties(ReadDBPathProperties.class)
public class NewDataSourceAop implements MethodBeforeAdvice {
private static final Logger log = LoggerFactory.getLogger(NewDataSourceAop.class); @Autowired
private ReadDBPathProperties readDBPathProperties; @Override
public void before(Method method, Object[] args, Object target) throws Throwable {
String clazzName = method.getDeclaringClass().getSimpleName();
String runner = clazzName + "." + method.getName();
this.chooseDataSource(runner);
} private void chooseDataSource(String runner){
runner += ",";
String read = readDBPathProperties.getReadPath()+",";
log.info("case : read path, vo : readPath = {}", read);
int index = read.indexOf(runner);
if (index == -1){
log.info("case : choose write DB, runner : {}, tid={}", runner, Thread.currentThread().getId());
HandleDataSource.putDataSource("write");
return;
}
log.info("case : choose read DB, runner : {}, tid={}", runner, Thread.currentThread().getId());
HandleDataSource.putDataSource("read");
}
}

  最后再特别说一下,关于这个功能的单元测试,这个单元测试有一点意思,如果是直接运行测试方法,启动过程和执行过程在同一个线程是测试不出效果的,因为启动过程中加载Bean的时候会对下图中的resources初始化,写入默认的数据源:

  就会导致后续执行的读写分离拦截器失效,只要保证执行线程和启动线程不在同一线程就好。

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      

Spring Boot 声明式事务结合相关拦截器的更多相关文章

  1. 使用注解实现Spring的声明式事务管理

    使用注解实现Spring的声明式事务管理,更加简单! 步骤: 1) 必须引入Aop相关的jar文件 2) bean.xml中指定注解方式实现声明式事务管理以及应用的事务管理器类 3)在需要添加事务控制 ...

  2. Spring(四)Spring JdbcTemplate&声明式事务

    JdbcTemplate基本使用 01-JdbcTemplate基本使用-概述(了解) JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装.spr ...

  3. 保护亿万数据安全,Spring有“声明式事务”绝招

    摘要:点外卖时,你只需考虑如何拼单:选择出行时,你只用想好目的地:手机支付时,你只需要保证余额充足.但你不知道这些智能的背后,是数以亿计的强大数据的支持,这就是数据库的力量.那么庞大数据的背后一定会牵 ...

  4. spring aop 声明式事务管理

    一.声明式事务管理的概括 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一. Spring的声明式事务顾名思义就是采用声明 ...

  5. Spring之声明式事务

    在讲声明式事务之前,先回顾一下基本的编程式事务 编程式事务: //1.获取Connection对象 Connection conn = JDBCUtils.getConnection(); try { ...

  6. 【Spring】——声明式事务配置详解

    项目中用到了spring的事务: @Transactional(rollbackFor = Exception.class, transactionManager = "zebraTrans ...

  7. Spring AOP声明式事务异常回滚(转)

    转:http://hi.baidu.com/iduany/item/20f8f8ed24e1dec5bbf37df7 Spring AOP声明式事务异常回滚 近日测试用例,发现这样一个现象:在业务代码 ...

  8. @Transactional、Spring的声明式事务

    传送门 一.Spring的声明式事务 需要在xml文件中配置 <!--配置事务管理器类--> <bean id="transactionManager" clas ...

  9. Spring -12 -声明式事务及完整的XML配置文件信息 -声明式事务中的相关属性(tx:advice的标签)

    1.编程式事务: 1.1由程序员编程事务控制代码. 1.2OpenSessionInView 就属于编程式事务: session.commit()和rollback() 2.声明式事务: 2.1事务控 ...

随机推荐

  1. jenkins+webhook+docker做持续集成

    简介:我们现在都流行把项目封装成docker的镜像,不过实际用的时候就会发现很麻烦,我们每次更改代码了以后都要打包成docker容器 ,事实证明项目比较多的时候真的会让人崩溃,我这边用spring c ...

  2. 最新windows 0day漏洞利用

    利用视屏:https://v.qq.com/iframe/player.html?vid=g0393qtgvj0&tiny=0&auto=0 使用方法 环境搭建 注意,必须安装32位p ...

  3. JS为网页添加文字水印【原创】

    最近需要实现为网页添加水印的功能,由于水印的信息是动态生成的,而百度谷歌上的方法往往都是为网页添加图片水印或为图片添加水印,而为网页添加文字水印相关资料较少,于是就自己动手写了这个代码. 通常加动态水 ...

  4. java多线程基本概述(四)——死锁

    package mytask; public class Task { public static void main(String[] args) { DeadThread thread = new ...

  5. Linux嵌入式开发中常用的两个工具

    TFTP 全称:Trivial File Transfer Protocol(简单文件传输协议) 进行小文件传输 在ubuntu下设置TFTP服务器 $ sudo apt-get install tf ...

  6. JavaWeb开发之Servlet

    1. Servlet有关概念和前置知识 1.1 什么是动态网页 如果浏览器在不同时刻或不同条件下访问web服务器上的某个页面,浏览器所获得的页面内容可以发生变化,那么这个页面就称之为动态页面. 动态网 ...

  7. iOS 播放GIf图, 动态效果

    一.如果你集成了SDWebImage , 有一个很简单的方法 //导入sdwebImage的某个头文件 #import "UIImage+GIF.h" _bubble1.backg ...

  8. 学习java分为几个阶段,分别是什么?

    多年前我自学的时候是很茫然,上网问问题,总是一堆外行的人说很难啊,你需要这样需要那样,不然就是,一堆人说一些空话,多看多写,买好书,我很无语,除了这些就没有自己的一些想法吗? 首先很多人认为学JAVA ...

  9. 【渗透测试】PHPCMS9.6.0 任意文件上传漏洞+修复方案

    这个漏洞是某司的一位前辈发出来的,这里只是复现一下而已. 原文地址:https://www.t00ls.net/thread-39226-1-1.html 首先我们本地搭建一个phpcms9.6.0的 ...

  10. java并发程序——BlockingQueue

    概述 BlockingQueue顾名思义'阻塞的队列',是指在:队列的读取行为被阻塞直到队列不为空时,队列的写入行为被阻塞直到队列不满时.BlockingQueue是java.util.concurr ...