@Transactional 注解失效情况及解决办法

2022-10-13  本文已影响0人  瞎胡扯1

一、@Transactional 注解在了非 public 方法上

  1. 如下所示@Transactional修饰在了非public方法上
@Service
public class TestServiceImpl {

    @Resource
    private Test1Mapper test1Mapper;
    
    @Transactional
     protected Long save(Long seq){
        Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
        Test1Entity entity = new Test1Entity().setSeq(seq);
        test1Mapper.insert(entity);
        long l = entity.getId() / 0;
        return entity.getId();
    }
}

  1. 失效原因

    Spring 中是通过动态代理来实现注解了@Transactional的方法的事务处理操作,而在处理 @Transactional 的注解时是只对 public方法才有效。
    Spring 在扫描切点时,是根据注解进行判断是否创建切点。

    AopUtils#canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)
    
    
    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        // .....
        for (Class<?> clazz : classes) {
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                if (introductionAwareMethodMatcher != null ?
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                        // 此处判断是否匹配生成切面
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    
    // TransactionAttributeSourcePointcut# matches(Method method, Class<?> targetClass)
    
    public boolean matches(Method method, Class<?> targetClass) {
        TransactionAttributeSource tas = getTransactionAttributeSource();
        // 获取 注解属性
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }
    
    
    // AnnotationTransactionAttributeSource#getTransactionAttribute
    // AnnotationTransactionAttributeSource#computeTransactionAttribute
    
    @Nullable
    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
        // Don't allow no-public methods as required.
        // 判断是否只允许 public 方法
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }
    
        // 其他代码 ...
        return null;
    }
    
    
  2. 解决方法

二、在类内部调用添加 @Transactional 的方法

  1. 实例
@Service
public class TestServiceImpl {

   @Resource
   private Test1Mapper test1Mapper;

   @Transactional
    public void save(Long seq){
       Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
       test1Mapper.insert(new Test1Entity(null, seq));
       long l = aLong / 0;
   }

   // 调用内部方法
   public void innerInvoke(){
       this.save(100L);
   }
}

  1. 失效原因

此种情况跟情况一的原因类似,在Spring启动时会扫描所有添加了@Transactional注解的类,创建的切面,当在同一个类中调用是,切面失去了作用。

  1. 解决方法

既然事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了。

如下所示:


@Service
public class TestServiceImpl {

    @Resource
    private Test1Mapper test1Mapper;

    // 第一个一个自己作为属性
    @Resource
    @Lazy
    private TestServiceImpl test1Service;

    @Transactional
     public void save(Long seq){
        Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
        test1Mapper.insert(new Test1Entity(null, seq));
        long l = aLong / 0;
    }

    // 调用内部方法
    public void innerInvoke(){
       //内部调用事务方法  
        test1Service.save(100L);
    }
}

三、捕获异常未抛出

  1. 实例

@Service
public class TestServiceImpl {

    @Resource
    private Test1Mapper test1Mapper;

    @Transactional
     public void save(Long seq){
        try {
            Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
            test1Mapper.insert(new Test1Entity(null, seq));
            long l = aLong / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  1. 失效原因

事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
这种的话,可能我们比较常见,问题就出在代理逻辑中,我们先看看源码里卖弄动态代理逻辑是如何为我们管理事务的。

TransactionAspectSupport#invokeWithinTransaction


 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  
          //异常时,在catch逻辑中回滚事务  
         completeTransactionAfterThrowing(txInfo, ex);  
         throw ex;  
      }  
      finally {  
         cleanupTransactionInfo(txInfo);  
      }  
       //提交事务  
      commitTransactionAfterReturning(txInfo);  
      return retVal;  
   }  
  
   else {  
     //....................  
   }  
} 

  1. 解决方法
上一篇下一篇

猜你喜欢

热点阅读