当前位置: 首页 > news >正文

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 类型的线程池,再获取不到,会获取名为 taskExecutorExecutor 类型的线程池,其实是由 TaskExecutionAutoConfiguration 自动注入的,可以通过 spring.task.execution.xxx 来更改其配置

2.2.2、方法二、使用AsyncConfigurer指定线程池

写一个类实现 AsyncConfigurer 接口,实现 getAsyncExecutorgetAsyncUncaughtExceptionHandler 方法,注意这个类要给 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注解源码解析),只需要知道这个注解导入的类 AsyncConfigurationSelectorString[] selectImports(AnnotationMetadata importingClassMetadata); 方法会在容器启动时执行,这个方法在其抽象父类 AdviceModeImportSelector 里,我们看下这个方法

image-20240724161425849

这里其实就是拿到 @EnableAsync 注解的 AdviceMode,再调用子类的 selectImports 方法,而 @EnableAsync 注解的 AdviceMode 的默认值是 AdviceMode.PROXY,再来看子类 AsyncConfigurationSelectorselectImports(AdviceMode adviceMode) 方法

image-20240724161955513

因为是 AdviceMode.PROXY,所以走的红框中的代码,我们继续看这个 ProxyAsyncConfiguration

image-20240724162908330

这个类里注册了一个 AsyncAnnotationBeanPostProcessor 类,并且调用了 configure 方法把 executorexceptionHandler 传入,这个executorexceptionHandler 是哪来的呢,在它的抽象父类 AbstractAsyncConfiguration 里赋的值,我们看下 AbstractAsyncConfigurationsetConfigurers 方法

image-20240724170943106

可以看到,就是我们之前 2.2.2 中用到的AsyncConfigurer,只要我们定义了实现了 AsyncConfigurer 接口的Bean,这里就把它的两个方法作为函数式接口赋值到 executorexceptionHandler 里,后面会用上

现在我们再回头看下 AsyncAnnotationBeanPostProcessor 的类图

image-20240724163645599

他是一个继承了 AbstractAdvisingBeanPostProcessor 抽象类的 BeanPostProcessor(想了解BeanPostProcessor的可以看我的另一篇文章:Spring后置处理器BeanFactoryPostProcessor与BeanPostProcessor源码解析),这个 AbstractAdvisingBeanPostProcessor 其实是 Spring AOP体系结构中非常重要的一个类,当我们想法实现一个切面的时候,可以扩展这个类,实现自己的Advisor,就可以在 postProcessAfterInitialization 方法里根据需要创建代理类,这里我们看看 AsyncAnnotationBeanPostProcessor 是如何实现这个 Advisor 的,可以在 AsyncAnnotationBeanPostProcessorsetBeanFactory 方法里找到,如下:

image-20240724170459036

这个创建了一个 AsyncAnnotationAdvisor,并把上文提到的 executorexceptionHandler 两个函数式接口传入 ,我们看下 AsyncAnnotationAdvisor 的这个构造函数

image-20240724171449116

可以看到构建了 advice 和 pointcut,这两个可以简单理解为 advice 定义了要执行的代码,而pointcut 定义了在哪里执行这些代码,这个 pointcut 很简单,我们可以到传进去的 Annotation 集合就是 Async,表示带 @Async 注解的就是切点,下面重点看下 advice,跟进下 buildAdvice 方法

image-20240724173421318

这里创建了 AnnotationAsyncExecutionInterceptor 并调用了 configure 方法,我们先看下 AnnotationAsyncExecutionInterceptor 的类图

image-20240724174103882

可以看到 AnnotationAsyncExecutionInterceptor 是实现了 MethodInterceptor 接口的,所以在调用被代理方法前,会先调用其 invoke 方法,我们在其父类 AsyncExecutionInterceptor 里找到这个 invoke 方法

image-20240724174730274

可以看到先获取 Executor,然后创线程任务,任务中调用了被代理的方法,最后把任务提交到线程池中,所以加上 @Async 注解的方法会在线程池中异步执行,下面我们重点看看这个 Executor 是怎么获取的,跟进 determineAsyncExecutor 方法

image-20240724175753253

可以看到,如果 @Async 后配置了线程池的名字,会从bean工厂里找对应的 Executor 返回,否则返回默认的 Executor,我们再来看默认的 Executor 是什么,回头看 AnnotationAsyncExecutionInterceptorconfigure 方法,在其父类 AsyncExecutionAspectSupport

image-20240724180050822

传进来的 defaultExecutorexceptionHandler 就是我们之前提到的 AsyncConfigurer 实现类的两个函数式接口,再贴个图,防止大家忘了

image-20240724170943106

defaultExecutor 如果没有,会调用 getDefaultExecutor 方法,exceptionHandler 如果没有,会默认使用 SimpleAsyncUncaughtExceptionHandler ,我们看下 getDefaultExecutor 方法

image-20240724180555577

先获取 TaskExecutor 类型的线程池,如果获取不到,会获取名为 taskExecutorExecutor 类型的线程池(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游戏的开发&#xf…...

Linux系统服务——【web,http协议,apache服务和nginx服务】(sixteen day)

一、web基础以及http协议 1、web基本概念和常识 前端开发一般用uniapp. 1、Web:为用户提供的一种在互联网上浏览信息的服务,Web 服务是动态的、可交互的、跨平台的和图形化的。 2、Web 服务为用户提供各种互联网服务,这些服务包括信息浏览服务&#xf…...

100、Python 关于时间日期的一些操作

在Python中,我们用于处理时间和日期相关的类型最常用的模块是datetime模块。该模块提供了很多与时间日期相关的类,对我们处理时间日期变得很方便。 以下是一些常见的关于时间日期的操作。 一、datetime类 1、获取当前日期和时间(年、月、日…...

【精通Redis】Redis命令详解

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

项目经理的开源工具指南:优化您的选择过程

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

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

负载均衡器》》LVS、Nginx、HAproxy 区别

虚拟主机 先4&#xff0c;后7...

ArcGIS Pro+ArcGIS给你的地图加上北回归线!

今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等&#xff0c;设置经线、纬线都以10间隔显示。 2、需要插入背会归线&#xf…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上

一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema&#xff0c;不需要复杂的查询&#xff0c;只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 &#xff1a;在几秒钟…...