Spring aop的异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚。默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚。
例如用户新增需要插入用户表和用户角色表,如果出现异常需要回滚所有操作以防止产生脏数据,解决方案如下:
方案1:service层统一抛出异常,如不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚 ,并且在Controller层统一捕获异常并处理。
try {
userDao.save(user);
saveUserRole(user);
} catch (Exception e) {
logger.info("新增用户接口异常,异常信息:"+e);
throw new RuntimeException();
}
方案2:在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常。
try {
userDao.save(user);
saveUserRole(user);
} catch (Exception e) {
logger.info("新增用户接口异常,异常信息:"+e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
方案3(推荐):在所在的方法上使用@Transactional注解,加上rollbackFor属性明确异常 ,形如:@Transactional(rollbackFor = Exception.class),实现异常回滚。
@Transactional(rollbackFor = Exception.class)
pubilc void saveUser(User user) throw Exception{
userDao.save(user);
saveUserRole(user);
}
使用@Transactional的注意事项:
1.可以被应用于接口定义、接口方法以及类、类的方法上, 但是Spring官方不建议接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外,@Transactional注解应该只被应用到public可见性上才不会无效,而且建议注解放在方法级别,即在实现类的方法上使用。
2.使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。
3.在方法里没有try catch抓异常时,被调用的方法入口处如果没有加事务注解,那么方法内调用其他的方法(不管其他方法前面有没有加事务注解),其他的方法的事务都不会生效;被调用的方法入口处如果加了事务注解,那么方法内调用其他的方法(不管其他方法前面有没有加事务注解),其他的方法的事务都会生效。
4. @Transactional 注解标识的方法,处理过程尽量的简单。尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。