[[Java动态代理]] [[Java注解与自定义注解]] [[Spring中利用AOP使用自定义注解]]
使用 @Transactional
, @Async
等Spring AOP注解不生效
问题背景
public class ServiceImpl implements Service{
// funA是接口暴露方法
@Override
public void funA(){
try{
funA();
}catch(Exception e){
Logger.infor(e);
}
}
// funB是接口暴露方法
@Override
@Transactional(rollbackFor = Exception.class)
public void funB(){
// 执行计算逻辑
funC();
}
public void funC(){
insertIntoHeader();
// 计算逻辑...
try{
// 计算出错
}catch(Exception e){
// 执行catch代码块
throw new CommonException("xxxx");
}
insertIntoLine();
}
}
以上代码出现了header插入成功,但是计算出错导致line插入失败的结果,分析过后发现其实是调用的时候没有使用动态代理对象来调用funB()导致funB()上面的 @Transactional(rollbackFor = Exception.class)
失效,从而使得整个 funA->funB->funC
流程都没有被spring事务管理器管理起来。写到这里,就必须把spring的AOP机制原理以及spring的aop常见问题记录下来了:首先必须要记住的一个点就是spring的AOP是通过动态代理完成,其次就是spring aop注解失效以及解决方案。
解释说明
通常情况,会出现Spring AOP注解失效主要是因为在同一个类中,funA调用给了funB(funB上加有注解),此时注解不生效。 针对所有的Spring AOP注解,Spring在扫描bean的时候发现有AOP注解,那么会动态的构造一个代理对象
- 如果是想要通过类的对象(通过spring依赖注入)直接调用其中带注解的funA时,此时funA的注解生效,因为此时spring会使用类的代理对象调用funA
- 假设类中的funA调用带有AOP注解的funB,而我们是通过对象(通过Spring依赖注入)调用funA的,那么funB上的注解是不生效的,因为此时调用funB使用的是原对象,而非代理对象,那么A在调用B时,在原对象内funB的注解当然时无效的。
解决方案:
- 把两个方法放到不同的类中
- 通过获取当前动态代理对象调用funcB:((X)AopContext.currentProxy()).B()
springboot下的分布式锁执行顺序
- 切面之间使用older注解,区分调用顺序,Order值越小,那么切面越先执行(越后结束)
- 不指定Order,那么Order是默认值->Integer.MAX_VALUE. 如果Order相同,则是按照切面字母的顺序来执行切面.比如@Transactional和@Cacheable->对应的切面是TransactionInterceptor和CacheInterceptor,则先执行@Cacheable的切面
- @Transactional也是通过切面实现,Order值是Integer.MAX_VALUE。(如果在service方法上同时添加带order的日志注解,在日志切面after里面报错,不会回滚事务)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}