我这项目的读写分离方式在使用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. Mybatis基础学习(一)—初识MyBatis

    一.MyBatis是什么?      MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google co ...

  2. 跟着刚哥梳理java知识点——集合(十二)

    Java集合分为Collection和Map两种体系 一.Collection接口: Collections接口为我们提供了以下方法: size():返回集合中元素的个数 add(Object obj ...

  3. 自动化构建工具gulp简单介绍及使用

    一.简介及安装: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成:使用她,我们不仅可以很愉快 ...

  4. Java NIO之通道

    一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...

  5. R语言写2048游戏

    2048 是一款益智游戏,只需要用方向键让两两相同的数字碰撞就会诞生一个翻倍的数字,初始数字由 2 或者 4 构成,直到游戏界面全部被填满,游戏结束. 编程时并未查看原作者代码,不喜勿喷. 程序结构如 ...

  6. Google Chrome 默认非安全端口列表

    1, // tcpmux7, // echo 9, // discard 11, // systat 13, // daytime 15, // netstat 17, // qotd 19, // ...

  7. Python 一行代码

    Python语法十分便捷,通过几个简单例子了解其趣味 1.Fizz.Buzz问题为: 打印数字1到100, 3的倍数打印"Fizz", 5的倍数打印"Buzz" ...

  8. Mac 下载安装MySQL

    step 1. 从官网上下载MySQL Community Server step 2. 安装MySQL step 3. 配置mysql和mysqladmin的alias $ vim ~/.bashr ...

  9. 蓝桥杯-组素数-java

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...

  10. base 镜像 - 每天5分钟玩转容器技术(10)

    上一节我们介绍了最小的 Docker 镜像,本节讨论 base 镜像. base 镜像有两层含义: 不依赖其他镜像,从 scratch 构建. 其他镜像可以之为基础进行扩展. 所以,能称作 base ...