问题描述:方法出错,导致事务回滚,然后AOP中使用@AfterThrowing注解来做方法出错后的处理,处理方式为将错误信息存入另一个数据库的表中。运行后报错,
错误信息一般为:Error updating database.Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Table 'test.logs' doesn't exist
,数据库表找不到,这是由于数据源没切换成功。
问题原因:
Spring中的事务是通过AOP来实现的,所以当我们自己写AOP拦截的时候,就会碰到事务和自定义AOP执行的先后顺序问题,如本文的动态切换数据源的问题。
这里我们使用Spring Order来定义AOP先后顺序,顺带稍微讲一下这个Spring Order。这里借用Ordered接口里的一句话Higher values are interpreted as lower priority。也就是值越高,优先级越低。反过来就是值越低,优先级越高。
但是这里有个问题,上句话说的越高越低和越低越高指的是在执行方法的前的AOP操作,AOP拦截是按照这个顺序,在执行方法后的AOP操作,反之顺序。
还有就是Spring数据源真正切换的关键是AbstractRoutingDataSource的determineTargetDataSource() 方法,此方法是在建立数据库连接时触发。
而事务是在connection层面管理的,启用事务后,一个事务内部的connection是复用的,所以就算AOP切了数据源字符串,但是数据源并不会被真正修改。
因为事务回滚和本文的@AfterThrowing都是在执行方法后,而且事务回滚顺序要在@AfterThrowing前面完成,否则数据源连接池会被事务锁住。按照上面的分析,这个事务回滚的Order值要大于这个自定义AOP中Order的值。
解决方案:
1、Aop切面类实现Ordered接口,实现getOrder()方法,如下
@Aspect
@Component
public class LogsAspect implements Ordered{
@Override
public int getOrder() {
// TODO Auto-generated method stub
return 1;
}
}
2、xml事务管理配置 order大于上面配置的1 或者不配置
<tx:annotation-driven transaction-manager="transactionManager" order="2" />
配置完上面两个步骤后,问题也就解决了。
讲到这里,本篇文章到此也就结束了,如果文章中有问题,或者有一些不够严谨完善的地方,希望大家体谅体谅。欢迎大家留言,交流交流。