后置增强this advice advises no methods_透过现象看原理:详解Spring中Bean的this调用导致AOP失效的原因...

本文深入探讨了在Spring中,为何在Bean内部使用`this`调用被@Async等注解的方法会导致注解不生效。分析了AOP动态代理的工作原理,特别是CGLIB的代理逻辑,并提供了通过ApplicationContext、BeanFactoryAware或AopContext获取动态代理对象以解决`this`调用注解失效问题的方法。
摘要由CSDN通过智能技术生成

前言

在我们使用Spring时,可能有前辈教导过我们,在bean中不要使用this来调用被@Async、@Transactional、@Cacheable等注解标注的方法,this下注解是不生效的。

那么大家可曾想过以下问题

  1. 为何致this调用的方法,注解会不生效
  2. 这些注解生效的原理又是什么
  3. 如果确实需要调用本类方法,且还需要注解生效,该怎么做?
  4. 代理是否可以做到this调用注解就直接生效?

通过本文,上面的疑问都可以解决,而且可以学到很多相关原理知识,信息量较大,那么就开始吧

现象

以@Async注解为例,@Async注解标记的方法,在执行时会被AOP处理为异步调用,调用此方法处直接返回,@Async标注的方法使用其他线程执行。

使用Spring Boot驱动

@SpringBootApplication@EnableAsyncpublic class Starter { public static void main(String[] args) { SpringApplication.run(Starter.class, args); }}@Componentpublic class AsyncService { public void async1() { System.out.println("1:" + Thread.currentThread().getName()); this.async2(); } @Async public void async2() { System.out.println("2:" + Thread.currentThread().getName()); }}@RunWith(SpringRunner.class) @SpringBootTest(classes = Starter.class)public class BaseTest { @Autowired AsyncService asyncService; @Test public void testAsync() { asyncService.async1(); asyncService.async2(); }}

输出内容为:

1:main2:main2:SimpleAsyncTaskExecutor-2

第一行第二行对应async1()方法,第三行对应async2()方法,可以看到直接使用asyncService.async2()调用时使用的线程为SimpleAsyncTaskExecutor,而在async1()方法中使用this调用,结果却是主线程,原调用线程一致。这说明@Async在this调用时没有生效。

思考&猜测

已知对于AOP动态代理,非接口的类使用的是基于CGLIB的动态代理,而CGLIB的动态代理,是基于现有类创建一个子类,并实例化子类对象。在调用动态代理对象方法时,都是先调用子类方法,子类方法中使用方法增强Advice或者拦截器MethodInterceptor处理子类方法调用后,选择性的决定是否执行父类方法。

那么假设在调用async1方法时,使用的是动态生成的子类的实例,那么this其实是基于动态代理的子类实例对象,this调用是可以被Advice或者MethodInterceptor等处理逻辑拦截的,那么为何理论和实际不同呢?

这里大胆推测一下,其实async1方法中的this不是动态代理的子类对象,而是原始的对象,故this调用无法通过动态代理来增强。

关于上面AOP动态代理使用CGLIB相关的只是,可以参考完全读懂Spring框架之AOP实现原理这篇文章。

下面开始详细分析。

源码调试分析原理

首先要弄清楚@Async是如何生效的:

1. 分析Async相关组件

从生效入口开始看,@EnableAsync注解上标注了@Import(AsyncConfigurationSelector.class)

@Import的作用是把后面的@Configuration类、ImportSelector类或者ImportBeanDefinitionRegistrar类中import的内容自动注册到ApplicationContext中。关于这三种可Import的类,这里先不详细说明,有兴趣的读者可以自行去Spring官网查看文档或者等待我的后续文章。

这里导入了AsyncConfigurationSelector,而AsyncConfigurationSelector在默

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值