背景
最近学了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里面继续抛出异常无效,所以失败(原因不详)