一文彻底讲透@Async注解的原理和使用方法

https://www.cnblogs.com/yangxiaohui227/p/14831911.html
一.背景:spring提供了@Async异步注解,使得方法的调用可以异步的进行,下面代码提供简单的演示:

@Configuration
@EnableAsync
@ComponentScan("com.yang.xiao.hui.aop")
public class App {public static void main(String[] args) {ApplicationContext ctx = new  AnnotationConfigApplicationContext(App.class);MyAsync service = ctx.getBean(MyAsync.class);System.out.println(service.getClass());service.async1();System.out.println("目标方法执行完没.........");}
}@Component
public class MyAsync {@Asyncpublic void async1() {try {TimeUnit.SECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello1:"+this.getClass());}public void async2(){System.out.println("hello2:"+this.getClass());this.async1();}
}

上述代码提供了最简单的异步使用方式,如果是同步执行,那么控制台打印的顺序应该是:
System.out.println(“hello1:”+this.getClass())-------》 System.out.println(“目标方法执行完没…”);
然而控制台的打印刚好相反,证明异步的注解生效了:

二.原理分析
1.猜想:aync1()方法标注了@Async注解,该方法就异步执行了,那么该方法肯定是被拦截了,方法拦截肯定存在一个方法拦截器MethodInterceptor

方法拦截器是一个接口,对异步方法的拦截,肯定是该接口的一个实现类,如何找到它:

2.线索分析:我们的唯一条件是主启动类上贴了一个@EnableAsync注解
点击进去分析:

根据追踪,我们发现,最终会往容器中注入 AsyncAnnotationBeanPostProcessor,
分析其继承体体系,发现其实现了BeanFactoryAware接口,实现该接口的类,spring容器在创建该bean时,会回调:void setBeanFactory(BeanFactory beanFactory) throws BeansException;

接着我们看看:

我们之后看看AsyncAnnotationAdvisor的创建过程:

接着看this.advice = buildAdvice(executor, exceptionHandler);

至此,我们终于找到方法拦截器了,为何是它,看看它的继承体系:

3.验证:既然找到了方法拦截器,那么我们就打断点在拦截方法里,执行之前的测试代码:拦截方法在它的父类中:AsyncExecutionInterceptor


原理非常简单:1.获取线程池 2.创建callable 3.执行线程
重点是线程池的获取逻辑:


由上所得:线程池会首先通过用户自己配置的为准:

所以,我们可以自定义线程池,然后注入spring中,将bean的名字放到@Async注解的value值即可

最后我们看看默认的线程池是怎么获取的:
targetExecutor = this.defaultExecutor.get();

可以知道,他是通过调用getDefaultExecutor(this.beanFactory)

由此可见,会先获取容器中TaskExecutor的线程池,获取不到,就获取指定beanName的线程池DEFAULT_TASK_EXECUTOR_BEAN_NAME = “taskExecutor”;

我们看看默认的线程池

总结:@Async的异步线程池获取顺序:

三:学以致用

直接创建2个异步方法:

四:思考:如果一个类中,非异步方法调用了异步的方法,异步方法还会生效么:

@Component
public class MyAsync {@Asyncpublic void async1() {try {TimeUnit.SECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello1:"+this.getClass());}public void async2(){System.out.println("hello2:"+this.getClass());this.async1();}
}@Configuration
@EnableAsync
@ComponentScan("com.yang.xiao.hui.aop")
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);MyAsync service = ctx.getBean(MyAsync.class);System.out.println(service.getClass());service.async2();System.out.println("目标方法执行完没.........");}
}

如果异步生效,那么System.out.println(“目标方法执行完没…”);会先于System.out.println(“hello1:”+this.getClass());执行

然而结果:

由此看到并不是同步执行,原因如下:
service.async2(); 方法调用:service是代理类,上图打印的第一行可以知道,它是cglib代理,因此该方法本质调用的是代理类的aync2(),而该方法并没有@aync注解也就没有方法拦截器,因此代理类执行async2()方法,本质最终是直接调用了被代理类的方法,所以上图打印出的第二行可以知道:

this.getClass()==com.yang.xiao.hui.aop.MyAsync

由此可以知道,只要是代理类调用async1()方法,就可以让异步调用生效了,如何获取被代理类,既然asyn2()是代理类调用的,肯定会被拦截,我们debug进入

当this.advised.exposeProxy=true时,代理类设置到AopContext中

原来是设置到ThreadLocal中,那成立的条件又是啥

该值其实就是@EnableAspectJAutoProxy(exposeProxy = true)的值

由此,改进我们的调用方法,通过ThreadLocal获取代理类,然后调用目标方法

启动测试:

居然报错了。。。。。,这么说,@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)并没有生效,那么我们看看该注解的配置原理是啥?



由此发现,它指对自动创建的的aop创建器生效,到底有哪些呢?

事务注解相关的就可以,但我们的是异步注解,根据之前源码分析知道,异步是通过AsyncAnnotationBeanPostProcessor来实现的;


由此,我们是否可以通过获取AsyncAnnotationBeanPostProcessor的beanDefiniton然后给它新增属性,类型下面的实现:


具体改造如下:

@Component
public class MyBeanDefinitionRegistryPostProcessor  implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {BeanDefinition beanDefinition = registry.getBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);if(null!=beanDefinition){beanDefinition.getPropertyValues().add("exposeProxy", Boolean.TRUE);}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

此时启动类的@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)可以不用了,再次测试:

上述就是同个类中调用异步方法不生效的解决方案

一文彻底讲透@Async注解的原理和使用方法相关推荐

  1. 一文彻底讲透电容—— 充放电曲线的秘诀

    笔者电子信息专业硕士毕业,获得过多次电子设计大赛.大学生智能车.数学建模国奖,现就职于南京某半导体芯片公司,从事硬件研发,电路设计研究.对于学电子的小伙伴,深知入门的不易,特开次博客交流分享经验,共同 ...

  2. 一文讲透自适应熔断的原理和实现

    为什么需要熔断 微服务集群中,每个应用基本都会依赖一定数量的外部服务.有可能随时都会遇到网络连接缓慢,超时,依赖服务过载,服务不可用的情况,在高并发场景下如果此时调用方不做任何处理,继续持续请求故障服 ...

  3. 4种多租户数据库设计方案对比及思考,一文全讲透

    文章目录 前言 一.设计方案 二.方案剖析 三.方案总结 四.方案选型 五.引申问题的解决方案 六.写在最后 前言 多租户是SaaS(Software-as-a-Service)下的一个概念,意思为软 ...

  4. 深度剖析未来网络服务模式 《云交换白皮书》一文全讲透

    历时超过3个月时间,经多方论证研究,由中国信通院.犀思云.应通科技联合撰写的<云交换白皮书>于2021年8月30日正式发布. 这是国内云计算领域首次对云交换这一细分领域,进行全面的系统性的 ...

  5. 超详细!一文讲透机器视觉常用的 3 种“目标识别”方法

    来源:机器视觉沙龙 随着机器视觉技术的快速发展,传统很多需要人工来手动操作的工作,渐渐地被机器所替代. 传统方法做目标识别大多都是靠人工实现,从形状.颜色.长度.宽度.长宽比来确定被识别的目标是否符合 ...

  6. SpringBoot利用@Async注解实现异步调用

    前言:异步编程是让程序并发运行的一种手段,使用异步编程可以大大提高我们程序的吞吐量,减少用户的等待时间.在Java并发编程中实现异步功能,一般是需要使用线程或者线程池.而实现一个线程,要么继承Thre ...

  7. Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例

    Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例 一.概述 在日常开发的工作中,经常会使用异步进行开发.Spring 提供一个简单 ...

  8. js打印线程id_一文讲透“进程,线程和协程”

    一文讲透"进程,线程和协程" 本文从操作系统原理出发结合代码实践讲解了以下内容: 什么是进程,线程和协程? 它们之间的关系是什么? 为什么说Python中的多线程是伪多线程? 不同 ...

  9. 10自带sftp服务器_一文讲透FTP和SFTP的区别

    阅读本文约需要10分钟,您可以先关注我们或收藏本文,避免下次无法找到. FTP和SFTP都是文件传输协议,我们知道FTP使用的是20和21端口,SFTP使用的是22端口.另外,SFTP前面的S应该是S ...

最新文章

  1. 黑鹰长期班.边程浪子系列教程
  2. LINUX不能ping域名, 能ping ip, 添加DNS解析
  3. java好还是python好-学Python 好还是java 好?
  4. 0x0F19B7EC (ucrtbased.dll)处(位于 ex6.exe 中)引发的异常: 0xC0000005: 写入位置 0x00740000 时发生访问冲突。...
  5. Spring boot的Restful风格CRUD
  6. 用生动的例子花式解释:python类中一定需要有 __init__方法么?没有会怎样?
  7. 天下为公:TCP堵塞控制
  8. i2c-toos 交互数据_什么是CD-i(交互式光盘)?
  9. 身价超13000亿!他又重回世界首富了
  10. python求均值标准差不用numpy_【Python】不用numpy用纯python求极差、平均数、中位数、众数与方差,python的打印...
  11. UIAlertView使用全解
  12. 和数传媒:区块链博弈论机制设计是未来关键
  13. kubectl源码分析之config get-contexts
  14. MVCC(多版本并发控制)原理
  15. Windows 小技巧10--Windows常见软件、系统配置
  16. 前端js,join()方法
  17. Java实现给图片局部打马赛克
  18. Ubuntu 16.04通过命令行连接Wi-Fi
  19. 【厚积薄发系列】C++项目总结19—组件化架构思想
  20. Jquery一个简单的注册验证

热门文章

  1. 微波——导引波(三)
  2. 作为一个码农,发发牢骚
  3. 一个程序员的面试经历(一)
  4. web利用腾讯云点播上传视频
  5. MLOps极致细节:18. Azure ML Pipeline(机器学习管道),Azure Container Instances (ACI)部署模型
  6. FMCW雷达在汽车自适应巡航中的应用(学习摘自MathWorks笔记)
  7. 不完美才美—少有人知道的幸福之路
  8. CentOS 7.3上图数据库Neo4j的安装和测试
  9. 常青藤爸爸完成5000万元A轮融资,打造四大版块核心业务生态
  10. python爬虫数据采集_python爬虫采集