Spring的Async注解线程池扩展方案

news2025/7/18 12:00:31

目录

  - [Spring的Async注解线程池扩展方案]

  - [目录]

  - [1. 扩展目的]

  - [2. 扩展实现]

  - [2.1 扩展Async注解的执行拦截器`AnnotationAsyncExecutionInterceptor`]

  - [2.2 扩展Async注解的Spring代理顾问`AsyncAnnotationAdvisor`]

  - [2.3 扩展Async注解的 Spring Bean 后置处理器`AsyncAnnotationBeanPostProcessor`]

  - [2.4 扩展代理异步配置类`ProxyAsyncConfiguration`]

  - [2.5 扩展异步代理配置选择器`AsyncConfigurationSelector`]

  - [2.6 扩展异步启动注解`@EnableAsync`]

  - [3. 额外扩展:给`@Async`注解代理指定线程池]

扩展目的

  1. 异步调用,改用Spring提供的`@Aysnc`注解实现,代替手写线程池执行。

  2. 在实际场景中,可能会遇到需要将主线程的一些个性化参数、变量、数据传递到子线程中使用的需求。

  3. `InheritableThreadLocal`可以解决子线程继承父线程值的需求,但是它存在一些问题。

  1. `SessionUser.SESSION_USER`是中台提供,无法修改。

  2. `InheritableThreadLocal`在线程池机制应用中并不友好,不及时在子线程中清除的话,会造成线程安全问题。

  实现思路有两种:

  1. 针对`ThreadLocal`进行扩展,并说服中台统一改用扩展后的`ThreadLocal`。

  2. 针对`@EnableAsync`和`@Async`注解进行扩展,将手动copy的代码写入到Spring代理类中。

  第一种要跟中台打交道,就很烦,能够天平自己独立解决,就自己解决。第二种会是一个不错的选择,扩展实现也并不困难。

  2. 扩展实现

  2.1 扩展Async注解的执行拦截器`AnnotationAsyncExecutionInterceptor`

  类全名:`org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor`

  从调试记录可以分析得出`AnnotationAsyncExecutionInterceptor#invoke`方法,正是创建异步任务并且执行异步任务的核心代码所在,我们要做的就是重写这个方法,将父线程的运行参数手动copy到子线程任务体中。

  2.2 扩展Async注解的Spring代理顾问`AsyncAnnotationAdvisor`

  我们依靠追踪`AnnotationAsyncExecutionInterceptor`的构造方法调用,定位到了它。

  全类名:`org.springframework.scheduling.annotation.AsyncAnnotationAdvisor`

  > 补充说明:代理顾问(`Advisor`)、建议(`Advice`)以及Spring代理实现原理

  >

  > Spring `@EnableAsync`默认的代理模式是 JDK 代理,代理机制如下:

  >

  > Spring 一个 Bean 会在 `BeanPostProcessor#postProcessAfterInitialization()`这个生命周期环节,遍历所有的`BeanPostProcessor`实例,判断Bean是否符合代理条件,如果符合代理条件,就给 Bean 代理对象中追加建议(`Advice`)对象,这样就完成了代理。

  >

  > 而建议(`Advice`)对象是由顾问(`Advisor`)对象创建和提供。

  >

  > 上一小节提到的异步执行拦截器`AnnotationAsyncExecutionInterceptor`就是实现了`Advice`接口的类。

  在`@Async`注解的代理过程中,异步执行拦截器`AnnotationAsyncExecutionInterceptor`就是通过`AsyncAnnotationAdvisor#buildAdvice`方法创建的。

  所以,当我们想要将扩展的新的异步执行拦截器`LibraAnnotationAsyncExecutionInterceptor`用起来,则需要相应的,还要把`AsyncAnnotationAdvisor#buildAdvice`方法重写。

  2.3 扩展Async注解的 Spring Bean 后置处理器`AsyncAnnotationBeanPostProcessor`

  我们依靠追踪`AsyncAnnotationAdvisor`的构造方法调用,定位到了它。

  类全名:`org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor`

  这个没什么好说的,Spring Bean 的生命周期其中一环。是 Spring Bean 实现代理的起点。

  开发人员可以自定义一个`BeanPostProcessor`类,把它注册到 Bean 容器中,它就会自动生效,并将后续的每一个 Bean 实例进行条件判断以及进行代理。

  我们要重写的方法是:`AsyncAnnotationBeanPostProcessor#setBeanFactory`。这个方法构造了异步代理顾问`AsyncAnnotationAdvisor`对象。

  2.4 扩展代理异步配置类`ProxyAsyncConfiguration`

  `AsyncAnnotationBeanPostProcessor`不是一般的 Spring Bean。它有几个限制,导致它不能直接通过`@Component`或者`@Configuration`来创建实例。

`AsyncAnnotationBeanPostProcessor`仅仅是实现了基于 JDK 代理,如果开发决定另外一种(基于ASPECTJ编织),那么它就应该受到某种条件判断来进行 Bean 实例化。

  2. `AsyncAnnotationBeanPostProcessor`还需要配置指定的线程池、排序等等属性,所以无法直接使用`@Component`注解注册为 Bean。

  我们阅读一下`@EnableAsync`注解源码:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AsyncConfigurationSelector.class)public@interfaceEnableAsync{Class<?extendsAnnotation>annotation()defaultAnnotation.class;booleanproxyTargetClass()defaultfalse;AdviceModemode()defaultAdviceMode.PROXY;intorder()defaultOrdered.LOWEST_PRECEDENCE;}
```

  进一步阅读`AsyncConfigurationSelector`的源码:

publicclassAsyncConfigurationSelectorextendsAdviceModeImportSelector<EnableAsync>{privatestaticfinalString ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME ="org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";/**
   * 分别为EnableAsync.mode()的PROXY和ASPECTJ值返回{@linkProxyAsyncConfiguration}或{@codeAspectJAsyncConfiguration} 。
     */@Override@NullablepublicString[]selectImports(AdviceMode adviceMode){switch(adviceMode){case PROXY:returnnewString[]{ProxyAsyncConfiguration.class.getName()};case ASPECTJ:returnnewString[]{ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};default:returnnull;}}}
```

  谜底揭晓,`ProxyAsyncConfiguration`原来是在这里开始注册到 Spring 容器中的。

  Spring Boot 启动后,会根据`@EnableAsync`注解的`mode()`方法的具体值,来决定整个Spring的 Bean 代理机制。

  既然 Spring 代理机制只会有一种,所以,也就只会在两种机制的配置类中选择其中一个来进行实例化。

  而默认`EnableAsync$mode()`默认值是`AdviceMode.PROXY`,所以默认采用 JDK 代理机制。

  2.5 扩展异步代理配置选择器`AsyncConfigurationSelector`

  类全名:`org.springframework.scheduling.annotation.AsyncConfigurationSelector`

  2.6 扩展异步启动注解`@EnableAsync`

  类全名:`org.springframework.scheduling.annotation.EnableAsync`

  3. 额外扩展:给`@Async`注解代理指定线程池

  `@Async`会自动根据类型`TaskExecutor.class`从 Spring Bean 容器中找一个已经实例化的异步任务执行器(线程池)。如果找不到,则另寻他路,尝试从 Spring Bean 容器中查找名称为`taskExecutor`的`Executor.class`实例。最后都还是未找到呢,就默认自动`new`一个`SimpleAsyncTaskExecutor`来用。

  > 补充说明:`TaskExecutor.class`是Spring定义的,而`Executor.class`JDK定义的。

  场景:其他小伙伴、或者旧代码已经实现过了一个线程池,但是这个线程池,是个`Executor.class`类型,且 Bean 实例名称不是`taskExecutor`(假设是`libraThreadPool`),正常情况下`@Async`根本无法找到它。

  需求:通过配置,将`@Async`的默认线程池,指定为名为`libraThreadPool`的`Executor.class`类型线程池。

  我们只需要注册一个实现`AsyncConfigurer`接口的配置类

  `org.springframework.scheduling.annotation.AbstractAsyncConfiguration#setConfigurers`:

/**
     * Collect any {@linkAsyncConfigurer} beans through autowiring.
     */@Autowired(required =false)voidsetConfigurers(Collection<AsyncConfigurer> configurers){if(CollectionUtils.isEmpty(configurers)){return;}if(configurers.size()>1){thrownewIllegalStateException("Only one AsyncConfigurer may exist");}AsyncConfigurer configurer = configurers.iterator().next();this.executor = configurer::getAsyncExecutor;this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;}
```

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/395790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux安装及管理应用和账号和权限管理 讲解

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…

不系统学习NodeJs之进程线程

不系统学习NodeJs之进程线程 从工作期间断断续续接触Node&#xff0c;从去年的3月份、10月份、又到今年的3月份&#xff0c;终于又决定要再仔细看看Node的相关。 不系统学习的各种时期笔记&参考记录于此。 参考&#xff1a; Node.js 中文网 一篇文章构建你的 NodeJS 知识…

BIC高颜值在线绘图新增颜色集合本地存储功能

用户可以把自己选择的颜色集合存储在自己浏览器本地(鼠标滑过颜色名字可以显示具体代表哪些颜色和色号)&#xff0c;供同一个工具的多套数据使用或者在不同工具之间共享颜色集合的使用&#xff0c;统一多张图一致的配色。最新访问地址&#xff1a;https://www.bic.ac.cn/BIC/ &…

JavaWeb—CSS

目录 &#xff11;、CSS 技术 &#xff11;.&#xff11;、CSS 技术介绍 &#xff11;.&#xff12;、CSS 语法规则&#xff1a; &#xff11;.&#xff13;、CSS 和 HTML 的结合方式 &#xff11;.&#xff13;.&#xff11;、第一种&#xff1a; &#xff11;.&#xf…

Iterator 迭代器

迭代器 为了兼顾 各个子类的特性 实现无差别可以 访问数据 举个例子 遍历数组和遍历链表 两者代码的写法不一样 为了实现 使用相同的代码 对不同的数据容器进行遍历 就出现了 迭代器 for语句的执行和 interator的实现息息相关 目的 访问各个类型 集合 的数据&#xff…

Spring源码解析-Spring 循环依赖

Spring源码解析简图&#xff1a; Spring 如何解决循环依赖&#xff0c;⽹上的资料很多&#xff0c;但是感觉写得好的极少&#xff0c;特别是源码解读⽅⾯&#xff0c;我就⾃⼰单独出⼀ 篇&#xff0c;这篇⽂章绝对肝&#xff01; 文章目录&#xff1a; 一. 基础知识 1.1 什么…

记录--elementui源码学习之仿写一个el-button

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 本篇文章记录仿写一个el-button组件细节&#xff0c;从而有助于大家更好理解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章&#xff0c;后续空闲了会不断更新并仿写其他…

Unity iOS 无服务器做一个排行榜 GameCenter

排行榜需求解决方案一(嗯目前只有一)UnityEngine.SocialPlatformsiOS GameCenterAppStoreConnect配置Unity 调用(如果使用GameCenter系统的面板&#xff0c;看到这里就可以了&#xff09;坑(需要获取数据做自定义面板的看这里)iOS代码Unity 代码吐槽需求 需求&#xff1a;接入…

某某游戏加密坐标分析

这个游戏里面坐标有很多种存放方式。 例如明文存放的DOUBLE&#xff0c;加密的各种类型。 我们不知道哪一个对于我们是有用的,哪一些只是辅助UI或则掉到LUA虚拟机坑里的数据。 那就根据作用大小来决定,一一尝试吧。 最好去找修改之后有效果的地址&#xff0c;当然只是本地&…

记一次影视cms黑盒CSRF-RCE

俗话说得好&#xff0c;思路才是最重要&#xff0c;本文章主要提供思路&#xff0c;各位师傅在挖掘漏洞的时候说不定也能碰到类似的点1.思路&#xff1a;当我们在找可以构建csrf的时候&#xff0c;多找找可以提交上传图片的&#xff0c;部分是可以自由构建url如图&#xff1a;漏…

Python数据分析案例20——我国家庭资产影响因素分析

本次案例较为简单&#xff0c;符合人文社科、经济学管理学等专业本科生适用。 本文的数据来源于中国家庭金融调查&#xff08;China Household Finance Survey&#xff0c;CHFS&#xff09;是西南财经大学中国家庭金融调查与研究中心&#xff08;下称中心&#xff09;在全国范围…

后端快速上手Vue+axios

文章目录前言vue基础1.el:挂载点2.data:数据对象vue常见指令vue生命周期axiosvueaxios前言 面向后端人员&#xff0c;旨在快速熟悉Vue框架&#xff0c;更详细的以后再总结 &#xff08;1&#xff09;Vue的特性&#xff1a; JavaScript框架简化Dom操作响应式数据驱动 &#…

JWT详细介绍使用

一、JWT介绍 JWT是JSON Web Token的缩写&#xff0c;即JSON Web令牌&#xff0c;是一种自包含令牌。 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息&#xff0c;以便于从资源服务…

【经验分享】电路板上电就挂?新手工程师该怎么检查PCB?

小伙伴们有没有经历过辛辛苦苦&#xff0c;加班加点设计的PCB&#xff0c;终于搞定下单制板。接下来焦急并且忐忑地等待PCB板到货&#xff0c;焊接&#xff0c;验证&#xff0c;一上电&#xff0c;结果直接挂了... 连忙赶紧排查&#xff0c;找问题。最终发现&#xff0c;是打过…

学习笔记:基于SpringBoot的牛客网社区项目实现(二)之Spring MVC入门

1.1 函数的返回值为空&#xff0c;因为可以使用response对象向浏览器返回数据。声明了request对象和response对象&#xff0c;dispatcherservlet自动将这两个对象传入 RequestMapping("/http")public void http(HttpServletRequest request, HttpServletResponse re…

ReentranLock(可重入锁)

一、ReentranLock ReentranLock属于JUC并发工具包下的类&#xff0c;相当于 synchronized具备如下特点 ● 可中断 ● 可以设置超时时间 ● 可以设置为公平锁&#xff08;防止线程出现饥饿的情况&#xff09; ● 支持多个条件变量 与 synchronized一样&#xff0c;都支持可重…

浅析 SplitChunksPlugin 及代码分割的意义

本文作者为 360 奇舞团前端开发工程师起因有同事分享webpack的代码分割&#xff0c;其中提到了SplitChunksPlugin&#xff0c;对于文档上的描述大家有着不一样的理解&#xff0c;所以打算探究一下。Q&#xff1a;什么是 SplitChunksPlugin&#xff1f;SplitChunksPlugin 是用来…

Python所有方向的入门和进阶路线,20年老师傅告诉你方法

干了20多年程序员&#xff0c;对于Python研究一直没停过&#xff0c;这几天把我自己对Python的认知和经验&#xff0c;再结合很多招聘网站上的技术要求&#xff0c;整理出了Python所有方向的学习路线图&#xff0c;基本上各个方向应该学什么&#xff0c;都在上面了&#xff0c;…

macOS 13.3 Beta 3 (22E5236f)发布

系统介绍3 月 8 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 13.3 开发者预览版 Beta 3 更新&#xff08;内部版本号&#xff1a;22E5236f&#xff09;&#xff0c;本次更新距离上次发布隔了 7 天。macOS Ventura 带来了台前调度、连续互通相机、FaceTime 通话接力…

文件预览kkFileView安装及使用

1 前言网页端一般会遇到各种文件&#xff0c;比如&#xff1a;txt、doc、docx、pdf、xml、xls、xlsx、ppt、pptx、zip、png、jpg等等。有时候我们不想要把文件下载下来&#xff0c;而是想在线打开文件预览 &#xff0c;这个时候如果每一种格式都需要我们去写代码造轮子去实现预…