spring事务失效场景

spring事务失效场景

首先spring的事务是使用AOP来实现的,而AOP的底层是代理(JDK代理或者CGLIB代理),所以事务失效就想什么时候不能进行代理

该描述的是使用注解@Transactional的方式来配置事务

  • 配置的方法非public修饰

    由于事务是使用的代理,而代理对于非public的方法不生效(private 不能被子类继承)

  • 配置的所在类非spring容器管理的bean

  • 注解修饰的方法被所在类使用this或默认调用

    Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效

    可以使用

    xml 复制代码
    <aop:config expose-proxy="true"></aop:config>

    来暴露代理类

    然后在代码中使用((MyServiceBiz)(AopContext.currentProxy())).test()来调用对应的方法

  • 默认情况下,业务抛出异常为非RuntimeException异常

    由于默认情况下只对于RuntimeException异常回滚

  • 业务代码使用try...catch捕获异常,然后直接消化了,并未抛出异常

  • 注解中设置了错误的传播方式

这里除了使用this调用外别的都比较好理解,只有this大家其实理解不了。举个例子

java 复制代码
public class Base {

    public void test(){
        System.out.println("Base#this执行");
    }

    public void method(){
        System.out.println("Base#method执行");
        this.test();
    }
}

public class Child extends Base{

    public void test(){
        System.out.println("Child#test执行");
    }


    public void invoke(){
        super.method();
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.invoke();
    }
}

// 执行结果为
Base#method执行
Child#test执行

就算是使用cglib来继承重写父类方法,在执行this.test()时也该执行的是Child的方法,而不该是Base的方法,除非在执行method()的对象时就已经是父类对象了,就像这样

java 复制代码
public class Child2 extends Base{

    public void test(){
        System.out.println("Child#test执行");
    }


    public void invoke(Base base){
        base.method();
    }

    public static void main(String[] args) {
        Child2 child = new Child2();
        Base base = new Base();
        child.invoke(base);
    }
}

// 执行结果为
Base#method执行
Base#this执行

这里来看一下spring是怎么实现的。看一下DynamicAdvisedInterceptor类,去掉与此次无关的代码

java 复制代码
// proxy是代理类
// method是被代理类的方法
// 
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Class<?> targetClass = null;
   Object target = null;
   try {
      // May be null. Get as late as possible to minimize the time we
      // "own" the target, in case it comes from a pool...
     // 被代理类对象
      target = getTarget();
      if (target != null) {
         targetClass = target.getClass();
      }
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
     // 没有代理的情况下,是直接执行方法的
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
        // 有代理,创建CglibMethodInvocation执行代理链
         // We need to create a method invocation...
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
}

在CglibMethodInvocation.proceed中看一下执行被代理方法是怎么执行的

java 复制代码
protected Object invokeJoinpoint() throws Throwable {
   if (this.publicMethod) {
     // 发现传入的其实是 被代理类对象,也就是说我们真正执行的是被代理类对象,aop的增强逻辑是在执行链的执行的,采用的是类似组合的方式来实现的
      return this.methodProxy.invoke(this.target, this.arguments);
   }
   else {
      return super.invokeJoinpoint();
   }
}

zhhll.icu/2021/框架/spr...

本文由mdnice多平台发布

相关推荐
洋不写bug几秒前
排序(一)基础排序,插入|希尔|冒泡|直接选择排序详解
java·算法·排序算法·插入排序·冒泡排序·希尔排序·直接选择排序
xcl09252 分钟前
全民健身智慧管理系统实战指南:从架构设计到部署落地
java·spring boot
Bs_MoneyMagnet6 分钟前
基于springboot+vue的宠物殡葬管理平台的设计与实现 源码+文档
java·javascript·vue.js·spring boot·后端·宠物
ly768914 分钟前
生产环境 Spring Boot 应用内存泄漏排查实战
java·spring boot·spring·内存泄漏·threadlocal·gc日志·堆转储
IT小白杨26 分钟前
eBay多账号如何应对关联判定:主体、收款、IP、环境四层配置清单一次讲清
java·网络·网络协议·tcp/ip·自动化·指纹浏览器
無a伟1 小时前
RabbitMq高级特性:TTL,死信队列,延迟队列
java·分布式·rabbitmq
长谷深风1111 小时前
Agent 跑了 30 分钟宕机,如何从断点继续?
java·大数据·人工智能·ai·大模型·memory·aiagent
慧都小妮子1 小时前
DevExpress Java 文档处理 API 免费 CTP:PDF、PowerPoint、条码与跨平台部署
java·pdf·powerpoint·devexpress·文档处理·条码生成
泡海椒1 小时前
告别反射低效:JQuick-Java ASM动态调用链性能优化实战
java·python·性能优化
土司大王2 小时前
LeetCode 17 电话号码的字母组合:Java 回溯模板、多叉决策树与复杂度分析
java·leetcode·决策树