spring事务

2年前 (2023) 程序员胖胖胖虎阿
179 0 0

一、spring事务失效的场景

1.捕获异常会导致事务失效

@Transactional等价于@Transactional(propagation = Propagation.REQUIRED),如果当前线程存在事务,则使用该事务,否则新建一个事务。

spring事务

 

spring事务

 spring事务

2.方法A中调用了方法B,方法A和B都开启事务,方法B的异常被try...catch...捕获,如果方法B发生异常,此时方法B中的事务会失效。

我们发现id=2和id=3的数据都插入成功了,说明save2方法中的事务是失效的。

spring事务

 

spring事务

 spring事务

 

简单分析一下原因:

事务失效的原因:jdk的动态代理导致。

//创建一个接口TestService

public interface TestService {


    public void save(); //核心业务方法

    public void save2();//非核心业务方法


}
//创建一个实现类TestServiceImpl

public class TestServiceImpl implements TestService {
    @Override
    public void save() {
        save2(); //在save方法中调用save2方法
        System.out.println("---------save---------");
    }

    @Override
    public void save2() {
        System.out.println("---------save2---------");
    }
}

//创建一个代理类MyProxyHandler,并测试结果
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyProxyHandler implements InvocationHandler {

    private Object target; //目标对象

    public MyProxyHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //代理需要做的额外的事情
        if(method.getName().startsWith("save")){
            System.out.println("代理对象需要做的额外的事情" + method.getName());
        }
        return method.invoke(target,args);
    }

    public static void main(String[] args) {
        MyProxyHandler myProxyHandler = new MyProxyHandler(new TestServiceImpl());

        TestService testService =  (TestService)Proxy.newProxyInstance(MyProxyHandler.class.getClassLoader(),new Class[]{TestService.class},myProxyHandler);
        testService.save();
        //testService.save2();
    }
}

结果如下:被代理的对象直接调用的方法才能被代理

spring事务

 解决方案:

//从AopContext或者从spring的应用上下文中获取代理对象即可
 AopContext.currentProxy()

3.事务方法访问修饰符非public,导致事务失败

4.@Transactional注解方法抛出的异常不是spring的事务支持的异常

    @Transactional
    public User save(User user) throws Exception {
        int save = userMapper.save(user);
        throw new Exception("手动抛出异常");
    }

spring事务

 解决方案:

1.手动指定异常类型,@Transactional(rollbackFor = Exception.class)

2.抛出RuntimeException或者spring事务支持的异常

5.@Transactional所在的类没有被spring管理会导致事务失效

6.传播类型不支持事务,导致事务失效

Propagation.NOT_SUPPORTED:表示不以事务运行,当前事务若存在事务则挂起

7.多线程调用会导致事务失效

两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务,我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交或回滚,如果不在同一个线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。

二、@Transactional原理

又到了源码环节了,手动滑稽。

在解析事务和执行事务需要了解的类和接口

TransactionInterceptor:事务拦截通知,主要作方法拦截,事务创建,业务方法执行。

TransactionAttributeSourcePointcut:切入点,检查这个方法有没有@Transactional注解

BeanFactoryTransactionAttributeSourceAdvisor:增强器,也就是切面和切入点的适配器

TransationAttributeSource:解析@Transactional注解中的属性(隔离级别,超时时间等等),然后缓存解析出来的事务属性

InfrastructureAdvisorAutoProxyCreator:这是一个后置处理bean,在spring实例化bean的时候生成代理对象

准备阶段:@EnableTransactionManagement开启注解事务做的事情,这个需要跟源码对照着一起看。

spring事务

spring事务 

 

spring事务

 事务初始化

其实我们需要弄清楚spring是如何生成代理对象的。

首先来回顾一下spring bean的生命周期

spring事务

 spring事务

 那我们是事务就是在执行初始化方法init-method后,通过BeanPostProcessor接口创建代理对象。

spring事务

 大胆猜想,小心验证。

首先是@Transactional,作用是定义代理植入点。【aop实现原理分析】中,分析知道代理对象创建的通过BeanPostProcessor的实现类AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInstantiation方法来实现个,如果需要进行代理,那么在这个方法就会返回一个代理对象给容器,同时判断植入点也是在这个方法中。

那么下面开始分析,在配置好注解驱动方式的事务管理之后,spring会在ioc容器创建一个BeanFactoryTransactionAttributeSourceAdvisor实例,这个实例可以看作是一个切点,在判断一个bean在初始化过程中是否需要创建代理对象,都需要验证一次BeanFactoryTransactionAttributeSourceAdvisor是否是适用这个bean的切点。如果是,就需要创建代理对象,并且把BeanFactoryTransactionAttributeSourceAdvisor实例注入到代理对象中。
 

spring事务

 

@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    //这里就是分析Method是否被@Transactional注解标注,有的话,不用说BeanFactoryTransactionAttributeSourceAdvisor适配当前bean,进行代理,并且注入切点
    //BeanFactoryTransactionAttributeSourceAdvisor
   AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
   if (attributes != null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null;
   }
}

上面就是判断是否需要根据@Transactional进行代理对象创建的判断过程。@Transactional的作用一个就是标识方法需要被代理,一个就是携带事务管理需要的一些属性信息。

动态代理逻辑实现

【aop实现原理分析】中知道,aop最终的代理对象的代理方法是

  • DynamicAdvisedInterceptor#intercept

所以我们可以在这个方法断点分析代理逻辑。

@Override
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 {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // 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();
      }
       //follow
      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 {
         // 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;
   }
   finally {
      if (target != null) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

最终是调用TransactionInterceptor#invoke方法,并且把CglibMethodInvocation注入到invoke方法中,从上面可以看到CglibMethodInvocation是包装了目标对象的方法调用的所有必须信息,因此,在TransactionInterceptor#invoke里面也是可以调用目标方法的,并且还可以实现类似@Around的逻辑,在目标方法调用前后继续注入一些其他逻辑,比如事务管理逻辑。

TransactionInterceptor–最终事务管理者

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
      throws Throwable {

   // If the transaction attribute is null, the method is non-transactional.
   final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass);

   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // Standard transaction demarcation with getTransaction and commit/rollback calls.
       //开启事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
         // This is an around advice: Invoke the next interceptor in the chain.
         // This will normally result in a target object being invoked.
          //方法调用
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // target invocation exception
   		//回滚事务
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
       //提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }

   else {
      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
         Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
               new TransactionCallback<Object>() {
                  @Override
                  public Object doInTransaction(TransactionStatus status) {
                     TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                     try {
                        return invocation.proceedWithInvocation();
                     }
                     catch (Throwable ex) {
                        if (txAttr.rollbackOn(ex)) {
                           // A RuntimeException: will lead to a rollback.
                           if (ex instanceof RuntimeException) {
                              throw (RuntimeException) ex;
                           }
                           else {
                              throw new ThrowableHolderException(ex);
                           }
                        }
                        else {
                           // A normal return value: will lead to a commit.
                           return new ThrowableHolder(ex);
                        }
                     }
                     finally {
                        cleanupTransactionInfo(txInfo);
                     }
                  }
               });

         // Check result: It might indicate a Throwable to rethrow.
         if (result instanceof ThrowableHolder) {
            throw ((ThrowableHolder) result).getThrowable();
         }
         else {
            return result;
         }
      }
      catch (ThrowableHolderException ex) {
         throw ex.getCause();
      }
   }
}

总结

最终可以总结一下整个流程,跟开始的猜想对照。

spring事务

 

博客仅供学习,如有侵权,请联系作者删除。

参考:Spring Bean的生命周期(非常详细) - Chandler Qian - 博客园

spring源码阅读--@Transactional实现原理_一撸向北的博客-CSDN博客_@transactional注解

版权声明:程序员胖胖胖虎阿 发表于 2023年3月20日 下午7:32。
转载请注明:spring事务 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...