Spring AOP 多个AOP和Spring事务绑定同一个方法

背景

最近学了Spring的AOP,Spring AOP是通过JDK和cglib的动态代理实现的。

而Spring的事务控制是基于AOP的,而我在学习Spring事务和AOP的时候将它们混合运行的时候

Spring的事务控制失效了,执行错误后不再进行回滚了,其它的AOP正常运行。

在网上参考这篇博客:
https://blog.csdn.net/qqxhwwqwq/article/details/51678595

以下是我较为拙劣的测试过程:

首先我们要准备两个自定义好的AOP

AOP1

@Component
@Aspect
@Order(1)
public class Aspectaop {

    @Pointcut("execution(* com.itheima.service.impl.AccountServiceImpl.*(..))")
    private void pt(){}

    /**
     * 环绕通知
     * @param pjp
     */
    @Around("pt()")
    public Object arroundLogger(ProceedingJoinPoint pjp) {
        Object rtValue = null;
        try {
            System.out.println("arroundLogger---1?前置通知");
            rtValue = pjp.proceed();
            System.out.println("arroundLogger---1?后置通知");
        } catch (Throwable throwable) {
            System.out.println("arroundLogger---1?异常通知");
            //throw new RuntimeException("arroundLogger---1 自定义AOP抛出异常让Spring事务catch异常");

//            throwable.printStackTrace();
        } finally {
            System.out.println("arroundLogger---1?最终通知");
            return rtValue;
        }
    }
}

AOP2

@Component
@Aspect
@Order(0)
public class Aspectaop2 {

    @Pointcut("execution(* com.itheima.service.impl.AccountServiceImpl.*(..))")
    private void pt(){}

    @Around("pt()")
    public Object arroundLogger2(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            System.out.println("arroundLogger---2?前置通知");
            rtValue = pjp.proceed();
            System.out.println("arroundLogger---2?后置通知");
        } catch (Throwable throwable) {
            System.out.println("arroundLogger---2?异常通知");
            throwable.printStackTrace();
        } finally {
            System.out.println("arroundLogger---2?最终通知");
            return rtValue;
        }
    }

}

另外还有一个Spring事务控制

@Service
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)//全局匹配
@Order(2)
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

    /**
     * 保持一个事务从而保持数据的原子性
     * @param sourceName
     * @param targetName
     * @param money
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false) //精确匹配
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("被增强函数执行...");
        //2、执行操作
        Account source = accountDao.findAccountByName(sourceName);
        Account target = accountDao.findAccountByName(targetName);

        source.setMoney(source.getMoney() - money);
        target.setMoney(target.getMoney() + money);

        accountDao.updateAccount(source);
        
        int i = 1/0; //抛出异常
        
        accountDao.updateAccount(target);
    }

}

好了,我们会注意到这三个类上面都有一个@Order注解,就是解决问题的关键,但不急,我们先

把它们注释掉,还原最初的形态跑一遍。

根据我Debug得到的执行顺序是

先执行Spring事务控制的Before,其次执行AOP1的Before, 再执行AOP2的Before,再执行被增强方法,

再执行AOP2的After, 再执行AOP1的After,最后执行Spring事务控制的After(有迭代的味道)

(这里借鉴一下参考博客的图哈~)

在这里插入图片描述

所以,按照这种执行顺序,此时 int i = 1/0 抛出的异常已经被AOP2 catch 到了,那么最外层的

Spring事务控制就无法 catch 到异常,所以Spring事务控制不进行回滚。

所以我们知道了原因就可以针对原因找出解决方案:

Spring事务控制无法回滚的原因:

异常被内部的AOP给catch了,导致Spring事务控制无法检测到异常而无法进行回滚

解决方案:

1、调整Spring事务控制、AOP1、AOP2的执行顺序,使Spring事务控制最后执行即可检测到异常

2、使AOP1、AOP2持续抛出新的异常,让Spring事务控制检测到异常而进行回滚


方案一:调整Spring事务控制、AOP1、AOP2的执行顺序

在Spring事务控制的类和自定义AOP上添加@Order注解

填写@Order中的序号标识AOP的执行顺序

我们将AOP2类上添加@Order(0),表示首先执行

AOP1类上添加@Order(1),表示第二执行

Spring事务控制的类上添加@Order(2),表示最后执行

如上面三个代码图所示

那么添加后的执行顺序就是

先执行AOP2的Before,其次执行AOP1的Before, 再执行Spring事务控制的Before,再执行被增强方法,

再执行Spring事务控制的After, 再执行AOP1的After,最后执行AOP2的After

那么抛出的异常Spring事务控制肯定检测得到,所以也成功回滚了!

但是奇怪的是Spring事务控制 catch 到了异常,外面一层的AOP1仍然捕获到了该异常,

于是我猜测Spring事务控制虽然 catch 到了异常,但是并没有处理,而是继续抛出了这个

异常让我们程序员去处理,所以AOP1仍然catch到了这个异常


方案二:(使AOP1、AOP2持续抛出新的异常,让Spring事务控制检测到异常而进行回滚)

未实现

因为尝试在AOP2中catch里面继续抛出异常无效,所以失败(原因不详)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值