Spring源码学习笔记之@Async源码
文章目录
- 一、简介
- 二、异步任务Async的使用方法
- 2.1、第一步、配置类上加@EnableAsync注解
- 2.2、第二步、自定义线程池
- 2.2.1、方法一、不配置自定义线程池使用默认线程池
- 2.2.2、方法二、使用AsyncConfigurer指定线程池
- 2.2.3、方法三、使用自定义的线程池Excutor
- 2.2.4、方法四、使用动态线程池来创建
- 2.3、第三步、在需要异步处理的方法上加@Async注解
- 三、源码解析
- 四、总结
一、简介
最近工作中接触到了 Spring 的 @Async
注解,有了了解其使用方法和源码的想法,所以有了这篇文章,本文源码来自Spring6.1.10
二、异步任务Async的使用方法
2.1、第一步、配置类上加@EnableAsync注解
在任意配置类上增加 @EnableAsync
注解,表示启用异步任务
@Configuration
@EnableAsync
public class MyConfig {
}
也可以加 SpringBoot 启动类上,因为 @SpringBootApplication
注解由 @Configuration
组成
2.2、第二步、自定义线程池
2.2.1、方法一、不配置自定义线程池使用默认线程池
如果不配置自定义的线程池,Spring会默认获取 TaskExecutor
类型的线程池,再获取不到,会获取名为 taskExecutor
的 Executor
类型的线程池,其实是由 TaskExecutionAutoConfiguration
自动注入的,可以通过 spring.task.execution.xxx
来更改其配置
2.2.2、方法二、使用AsyncConfigurer指定线程池
写一个类实现 AsyncConfigurer
接口,实现 getAsyncExecutor
和 getAsyncUncaughtExceptionHandler
方法,注意这个类要给 Spring 托管,所以要加上 @Component
注解
@Component
public class MyAsyncConfigurer implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程数executor.setCorePoolSize(5); //最大线程数executor.setMaxPoolSize(10); //队列容量executor.setQueueCapacity(200); //允许线程空闲时间(秒)executor.setKeepAliveSeconds(10);//线程名称前缀executor.setThreadNamePrefix("custom-"); executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {//异步任务未被捕获时的处理return new SimpleAsyncUncaughtExceptionHandler();}
}
2.2.3、方法三、使用自定义的线程池Excutor
不论是方法一还是方法二都有一个弊端,那就是所有的异步任务都会使用同一个线程池,所以可以使用方法三来定义多个线程池,通过实例 Bean 的方式把 Excutor
注入 Spring,并指定 Bean 的名称
@Configuration
public class CustomThreadPoolConfig {@Bean(name = "customExecutor")public ThreadPoolTaskExecutor customExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程数executor.setCorePoolSize(5); //最大线程数executor.setMaxPoolSize(10); //队列容量executor.setQueueCapacity(200); //允许线程空闲时间(秒)executor.setKeepAliveSeconds(10);//线程名称前缀executor.setThreadNamePrefix("custom-"); executor.initialize();return executor;}
}
2.2.4、方法四、使用动态线程池来创建
使用 dynamic-tp
动态线程池配置,这里就不展开了,有兴趣的可以去查阅资料,原理就是把 2.2.3
的 Bean 放到了配置文件里,并且可以动态改变参数
2.3、第三步、在需要异步处理的方法上加@Async注解
最后再需要异步处理的方法上增加 @Async
注解
@Service
public class MyServiceImpl implements MyService {@Asyncpublic void asyncMethod() {log.info("test");}}
如果选用 2.2.3
或者 2.2.4
的话,还需要在 @Async
上指定线程池的名称
@Service
public class MyServiceImpl implements MyService {@Async("customExecutor")public void asyncMethod() {log.info("test");}}
三、源码解析
先从 @EnableAsync
注解开始
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
}
可以看到通过 @Import
注解导入了 AsyncConfigurationSelector
类,这里不展开讲 @Import
注解了(想了解@Import注解的可以看我的另一篇文章:@Import注解源码解析),只需要知道这个注解导入的类 AsyncConfigurationSelector
的 String[] selectImports(AnnotationMetadata importingClassMetadata);
方法会在容器启动时执行,这个方法在其抽象父类 AdviceModeImportSelector
里,我们看下这个方法
这里其实就是拿到 @EnableAsync
注解的 AdviceMode
,再调用子类的 selectImports
方法,而 @EnableAsync
注解的 AdviceMode
的默认值是 AdviceMode.PROXY
,再来看子类 AsyncConfigurationSelector
的 selectImports(AdviceMode adviceMode)
方法
因为是 AdviceMode.PROXY
,所以走的红框中的代码,我们继续看这个 ProxyAsyncConfiguration
这个类里注册了一个 AsyncAnnotationBeanPostProcessor
类,并且调用了 configure 方法把 executor
和 exceptionHandler
传入,这个executor
和 exceptionHandler
是哪来的呢,在它的抽象父类 AbstractAsyncConfiguration
里赋的值,我们看下 AbstractAsyncConfiguration
的 setConfigurers
方法
可以看到,就是我们之前 2.2.2
中用到的AsyncConfigurer
,只要我们定义了实现了 AsyncConfigurer
接口的Bean,这里就把它的两个方法作为函数式接口赋值到 executor
和 exceptionHandler
里,后面会用上
现在我们再回头看下 AsyncAnnotationBeanPostProcessor
的类图
他是一个继承了 AbstractAdvisingBeanPostProcessor
抽象类的 BeanPostProcessor
(想了解BeanPostProcessor
的可以看我的另一篇文章:Spring后置处理器BeanFactoryPostProcessor与BeanPostProcessor源码解析),这个 AbstractAdvisingBeanPostProcessor
其实是 Spring AOP体系结构中非常重要的一个类,当我们想法实现一个切面的时候,可以扩展这个类,实现自己的Advisor
,就可以在 postProcessAfterInitialization
方法里根据需要创建代理类,这里我们看看 AsyncAnnotationBeanPostProcessor
是如何实现这个 Advisor
的,可以在 AsyncAnnotationBeanPostProcessor
的 setBeanFactory
方法里找到,如下:
这个创建了一个 AsyncAnnotationAdvisor
,并把上文提到的 executor
和 exceptionHandler
两个函数式接口传入 ,我们看下 AsyncAnnotationAdvisor
的这个构造函数
可以看到构建了 advice 和 pointcut,这两个可以简单理解为 advice 定义了要执行的代码,而pointcut 定义了在哪里执行这些代码,这个 pointcut
很简单,我们可以到传进去的 Annotation
集合就是 Async
,表示带 @Async
注解的就是切点,下面重点看下 advice,跟进下 buildAdvice
方法
这里创建了 AnnotationAsyncExecutionInterceptor
并调用了 configure
方法,我们先看下 AnnotationAsyncExecutionInterceptor
的类图
可以看到 AnnotationAsyncExecutionInterceptor
是实现了 MethodInterceptor
接口的,所以在调用被代理方法前,会先调用其 invoke
方法,我们在其父类 AsyncExecutionInterceptor
里找到这个 invoke
方法
可以看到先获取 Executor
,然后创线程任务,任务中调用了被代理的方法,最后把任务提交到线程池中,所以加上 @Async
注解的方法会在线程池中异步执行,下面我们重点看看这个 Executor
是怎么获取的,跟进 determineAsyncExecutor
方法
可以看到,如果 @Async
后配置了线程池的名字,会从bean工厂里找对应的 Executor
返回,否则返回默认的 Executor
,我们再来看默认的 Executor
是什么,回头看 AnnotationAsyncExecutionInterceptor
的 configure
方法,在其父类 AsyncExecutionAspectSupport
里
传进来的 defaultExecutor
和 exceptionHandler
就是我们之前提到的 AsyncConfigurer
实现类的两个函数式接口,再贴个图,防止大家忘了
defaultExecutor
如果没有,会调用 getDefaultExecutor
方法,exceptionHandler
如果没有,会默认使用 SimpleAsyncUncaughtExceptionHandler
,我们看下 getDefaultExecutor
方法
先获取 TaskExecutor
类型的线程池,如果获取不到,会获取名为 taskExecutor
的 Executor
类型的线程池(DEFAULT_TASK_EXECUTOR_BEAN_NAME = “taskExecutor”)
四、总结
其实 @Async 注解就是利用 Spring AOP 给类加了代理,当需要执行带 @Async
的方法时,会将其包装成 task 提交到线程池中异步执行,如果在 @Async
注解上定义线程池的名字,会用对应的线程池执行,否则使用 AsyncConfigurer
实现类中的 getAsyncExecutor
方法返回的 Executor
执行,如果未配置 AsyncConfigurer
实现类,则使用 TaskExecutionAutoConfiguration
配置类创建的 Executor
执行
相关文章:

Spring源码学习笔记之@Async源码
文章目录 一、简介二、异步任务Async的使用方法2.1、第一步、配置类上加EnableAsync注解2.2、第二步、自定义线程池2.2.1、方法一、不配置自定义线程池使用默认线程池2.2.2、方法二、使用AsyncConfigurer指定线程池2.2.3、方法三、使用自定义的线程池Excutor2.2.4、方法四、使用…...
面试题:如何验证代码的可靠性
代码结构上的: 1 可扩展性 是否否和开闭原则 2 性能,数据结构用的是否合理,算法等是否效率高。 3 安全性 是否存在潜在的安全 整数溢出 SQL注入 等 4 代码复杂度 圈负杂度 if嵌套深度 函数长度等 5 函数变量的命名是否具有自解释性 1. …...

前端开发的十字路口,薪的出口会是AI吗?
前言 在数字化转型的浪潮中,前端开发一直扮演着至关重要的角色,它连接着用户与产品之间的桥梁。然而,随着技术的不断进步和社会经济环境的变化,前端开发领域也面临着前所未有的挑战和机遇。 前端开发的困境 前端开发领域的竞争…...

pdf太大怎么压缩大小?这几种压缩方法操作起来很简单!
pdf太大怎么压缩大小?在数字化洪流席卷的当下,PDF文件的“臃肿”难题如同巨石般横亘于高效办公之路,它们不仅贪婪地吞噬着宝贵的存储空间,更如沉重的枷锁,拖曳着我们的工作进度,步入迟缓之境,试…...

leetcode-148. 排序链表
题目描述 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 1: 输入:head [4,2,1,3] 输出:[1,2,3,4]示例 2: 输入:head [-1,5,3,4,0] 输出:[-1,0,3,4,5]示例 3&#x…...
16 html网页服务和nginx服务
第十六次7.29 1.静态页面 1安装httpd [rootweb ~]# yum -y install httpd 2.真机访问页面 [rootweb html]# echo "静态html文件" > index.html 传入照片再次访问 静态资源,根据开发着保存在项目资源目录中的路径访问静态页面的资源 2.Apache 1.安…...

C语言:扫雷游戏实现
一、扫雷游戏的分析和设计 扫雷游戏想必大家都玩过吧,初级的玩法是在一个9*9的棋盘上找到没有雷的格子,而今天我们就要做的就是9*9扫雷游戏的实现。 1、游戏功能和规则 使用控制台实现经典的扫雷游戏游戏可以通过菜单实现继续玩或者退出游戏扫雷的棋盘…...

算法入门:Java实现排序、查找算法
链接:算法入门:Java实现排序、查找算法 (qq.com) 冒泡/选择/插入/希尔排序代码 (qq.com) 快排/归并/堆排/基数排序代码 (qq.com)...

【初阶数据结构篇】顺序表的实现(赋源码)
文章目录 本篇代码位置顺序表和链表1.线性表2.顺序表2.1 概念与结构2.2分类2.2.1 静态顺序表2.2.2 动态顺序表 2.3 动态顺序表的实现2.3.1动态顺序表的初始化和销毁及打印2.3.2动态顺序表的插入动态顺序表的尾插动态顺序表的头插动态顺序表的在指定位置插入数据 2.3.3动态顺序表…...

移动式气象站:便携科技的天气守望者
在科技日新月异的今天,我们身边的许多设备都在向着更加智能化、便携化的方向发展。而在气象观测领域,移动式气象站的出现,不仅改变了传统气象观测的固有模式,更以其灵活性和实时性,在气象监测、灾害预警等领域发挥着越…...

软件测试必备 - 14个接口与自动化测试练习网站
随着互联网和移动应用的快速发展,接口和自动化测试的重要性日益凸显。越来越多的企业开始重视API测试,因为它不仅能提升开发效率,还能确保系统的稳定性和安全性。这些练习网站为测试人员提供了宝贵的资源,帮助他们掌握必要的技能,应对日益复杂的测试需求。 在软件测试的世…...

基于 HTML+ECharts 实现的数据可视化大屏案例(含源码)
数据可视化大屏案例:基于 HTML 和 ECharts 的实现 数据可视化已成为企业决策和业务分析的重要工具。通过直观、动态的图表展示,数据可视化大屏能够帮助用户快速理解复杂的数据关系,发现潜在的业务趋势。本文将介绍如何利用 HTML 和 ECharts 实…...

vardaccico前端私有库
vardacico docker pull verdaccio/verdaccio:4 docker run -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio Docker | Verdaccio 拷贝docker中的配置到宿主机 进入docker内部 docker exec -it verdaccio /bin/sh 进入到指定目录 cd /verdaccio 开始拷贝到指定目…...

先用先发!小样本故障诊断新思路!Transformer-SVM组合模型多特征分类预测/故障诊断(Matlab)
先用先发!小样本故障诊断新思路!Transformer-SVM组合模型多特征分类预测/故障诊断(Matlab) 目录 先用先发!小样本故障诊断新思路!Transformer-SVM组合模型多特征分类预测/故障诊断(Matlab&#…...

学习大数据DAY26 简单数据清洗练习和 Shell 脚本中的数据库编程
目录 上机练习 14 mysql 命令 sql 语句实现步骤 shell 脚本导入 csv 格式文件到 mysql 数据库 secure-file-priv 特性 把文件拷贝到 mysql 指定目录下 上机练习 15 mysqldump 命令 上机练习 16 上机练习 14 运用上一节课学的 Shell 工具完成 1. 清洗数据《infotest.t…...

开发业务(3)——swoole和聊天室入门开发
在普通的PHP代码里面,我们不需要考虑性能和异步问题,包括不限于我们想要使用php搭建一个http服务器(在node/python/go里面都有http模块,但是PHP没有这种功能)。而同样的原因,很难实现php游戏的开发…...

Linux系统服务——【web,http协议,apache服务和nginx服务】(sixteen day)
一、web基础以及http协议 1、web基本概念和常识 前端开发一般用uniapp. 1、Web:为用户提供的一种在互联网上浏览信息的服务,Web 服务是动态的、可交互的、跨平台的和图形化的。 2、Web 服务为用户提供各种互联网服务,这些服务包括信息浏览服务…...
100、Python 关于时间日期的一些操作
在Python中,我们用于处理时间和日期相关的类型最常用的模块是datetime模块。该模块提供了很多与时间日期相关的类,对我们处理时间日期变得很方便。 以下是一些常见的关于时间日期的操作。 一、datetime类 1、获取当前日期和时间(年、月、日…...

【精通Redis】Redis命令详解
引言 Redis是一个内存数据库,在学习它的内部原理与实现之前,我们首先要做到的就是学会使用,学会其丰富的命令操作。 一、字符串 Redis的字符串类型之前笔者的一篇入门介绍中曾经说过,不是简单的只存人可以阅读的字符串…...

项目经理的开源工具指南:优化您的选择过程
国内外主流的10款开源项目管理系统对比:PingCode、Worktile、禅道、Teambition、Gogs、码云 Gitee、Jira、Redmine、ProjectLibre、OpenProject。 在选择合适的开源项目管理系统时,很多团队面临诸多挑战:功能是否全面?易用性如何&…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...