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

一、@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. 解决方法

  • 方法一:把需要支持事务的方法统一定义为 public 修饰的方法
  • 方法二:创建一个全为public修饰的方法的门面类,在此类中添加@Transactional

二、在类内部调用添加 @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. 解决方法
  • 方法一:在方法中不对异常进行捕获
  • 方法二:若必须捕获,在捕获处理后在重新抛出异常。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值