一文彻底讲透@Async注解的原理和使用方法
一文彻底讲透@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注解的原理和使用方法相关推荐
- 一文彻底讲透电容—— 充放电曲线的秘诀
笔者电子信息专业硕士毕业,获得过多次电子设计大赛.大学生智能车.数学建模国奖,现就职于南京某半导体芯片公司,从事硬件研发,电路设计研究.对于学电子的小伙伴,深知入门的不易,特开次博客交流分享经验,共同 ...
- 一文讲透自适应熔断的原理和实现
为什么需要熔断 微服务集群中,每个应用基本都会依赖一定数量的外部服务.有可能随时都会遇到网络连接缓慢,超时,依赖服务过载,服务不可用的情况,在高并发场景下如果此时调用方不做任何处理,继续持续请求故障服 ...
- 4种多租户数据库设计方案对比及思考,一文全讲透
文章目录 前言 一.设计方案 二.方案剖析 三.方案总结 四.方案选型 五.引申问题的解决方案 六.写在最后 前言 多租户是SaaS(Software-as-a-Service)下的一个概念,意思为软 ...
- 深度剖析未来网络服务模式 《云交换白皮书》一文全讲透
历时超过3个月时间,经多方论证研究,由中国信通院.犀思云.应通科技联合撰写的<云交换白皮书>于2021年8月30日正式发布. 这是国内云计算领域首次对云交换这一细分领域,进行全面的系统性的 ...
- 超详细!一文讲透机器视觉常用的 3 种“目标识别”方法
来源:机器视觉沙龙 随着机器视觉技术的快速发展,传统很多需要人工来手动操作的工作,渐渐地被机器所替代. 传统方法做目标识别大多都是靠人工实现,从形状.颜色.长度.宽度.长宽比来确定被识别的目标是否符合 ...
- SpringBoot利用@Async注解实现异步调用
前言:异步编程是让程序并发运行的一种手段,使用异步编程可以大大提高我们程序的吞吐量,减少用户的等待时间.在Java并发编程中实现异步功能,一般是需要使用线程或者线程池.而实现一个线程,要么继承Thre ...
- Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例
Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例 一.概述 在日常开发的工作中,经常会使用异步进行开发.Spring 提供一个简单 ...
- js打印线程id_一文讲透“进程,线程和协程”
一文讲透"进程,线程和协程" 本文从操作系统原理出发结合代码实践讲解了以下内容: 什么是进程,线程和协程? 它们之间的关系是什么? 为什么说Python中的多线程是伪多线程? 不同 ...
- 10自带sftp服务器_一文讲透FTP和SFTP的区别
阅读本文约需要10分钟,您可以先关注我们或收藏本文,避免下次无法找到. FTP和SFTP都是文件传输协议,我们知道FTP使用的是20和21端口,SFTP使用的是22端口.另外,SFTP前面的S应该是S ...
最新文章
- 黑鹰长期班.边程浪子系列教程
- LINUX不能ping域名, 能ping ip, 添加DNS解析
- java好还是python好-学Python 好还是java 好?
- 0x0F19B7EC (ucrtbased.dll)处(位于 ex6.exe 中)引发的异常: 0xC0000005: 写入位置 0x00740000 时发生访问冲突。...
- Spring boot的Restful风格CRUD
- 用生动的例子花式解释:python类中一定需要有 __init__方法么?没有会怎样?
- 天下为公:TCP堵塞控制
- i2c-toos 交互数据_什么是CD-i(交互式光盘)?
- 身价超13000亿!他又重回世界首富了
- python求均值标准差不用numpy_【Python】不用numpy用纯python求极差、平均数、中位数、众数与方差,python的打印...
- UIAlertView使用全解
- 和数传媒:区块链博弈论机制设计是未来关键
- kubectl源码分析之config get-contexts
- MVCC(多版本并发控制)原理
- Windows 小技巧10--Windows常见软件、系统配置
- 前端js,join()方法
- Java实现给图片局部打马赛克
- Ubuntu 16.04通过命令行连接Wi-Fi
- 【厚积薄发系列】C++项目总结19—组件化架构思想
- Jquery一个简单的注册验证
热门文章
- 微波——导引波(三)
- 作为一个码农,发发牢骚
- 一个程序员的面试经历(一)
- web利用腾讯云点播上传视频
- MLOps极致细节:18. Azure ML Pipeline(机器学习管道),Azure Container Instances (ACI)部署模型
- FMCW雷达在汽车自适应巡航中的应用(学习摘自MathWorks笔记)
- 不完美才美—少有人知道的幸福之路
- CentOS 7.3上图数据库Neo4j的安装和测试
- 常青藤爸爸完成5000万元A轮融资,打造四大版块核心业务生态
- python爬虫数据采集_python爬虫采集