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

目录

Spring的Async注解线程池扩展方案目录1. 扩展目的2. 扩展实现2.1 扩展Async注解的执行拦截器<AnnotationAsyncExecutionInterceptor2.2 扩展Async注解的Spring代理顾问<AsyncAnnotationAdvisor2.3 扩展Async注解的 Spring Bean 后置处理器<AsyncAnnotationBeanPostProcessor2.4 扩展代理异步配置类<ProxyAsyncConfiguration2.5 扩展异步代理配置选择器<AsyncConfigurationSelector2.6 扩展异步启动注解<@EnableAsync3. 额外扩展:给<@Async注解代理指定线程池

1. 扩展目的

异步调用,改用Spring提供的<@Aysnc注解实现,代替手写线程池执行。在实际场景中,可能会遇到需要将主线程的一些个性化参数、变量、数据传递到子线程中使用的需求。<InheritableThreadLocal可以解决子线程继承父线程值的需求,但是它存在一些问题。<SessionUser.SESSION_USER是中台提供,无法修改。<InheritableThreadLocal在线程池机制应用中并不友好,不及时在子线程中清除的话,会造成线程安全问题。

实现思路有两种:

针对<ThreadLocal进行扩展,并说服中台统一改用扩展后的<ThreadLocal针对<@EnableAsync<@Async注解进行扩展,将手动copy的代码写入到Spring代理类中。

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

2. 扩展实现

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

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

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

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

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 实例化。<AsyncAnnotationBeanPostProcessor还需要配置指定的线程池、排序等等属性,所以无法直接使用<@Component注解注册为 Bean。

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

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AsyncConfigurationSelector.class)public @interface EnableAsync {    Class<? extends Annotation> annotation() default Annotation.class;    boolean proxyTargetClass() default false;    AdviceMode mode() default AdviceMode.PROXY;    int order() default Ordered.LOWEST_PRECEDENCE;}

进一步阅读<AsyncConfigurationSelector的源码:

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";    /**   * 分别为EnableAsync.mode()的PROXY和ASPECTJ值返回{@link ProxyAsyncConfiguration}或{@code AspectJAsyncConfiguration} 。     */    @Override    @Nullable    public String[] selectImports(AdviceMode adviceMode) {        switch (adviceMode) {            case PROXY:                return new String[] {ProxyAsyncConfiguration.class.getName()};            case ASPECTJ:                return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};            default:                return null;        }    }}

谜底揭晓,<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.classJDK定义的。

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

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

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

<org.springframework.scheduling.annotation.AbstractAsyncConfiguration#setConfigurers

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

本文来自投稿,不代表重蔚自留地立场,如若转载,请注明出处https://www.cwhello.com/262776.html

如有侵犯您的合法权益请发邮件951076433@qq.com联系删除

(0)
黑马程序员黑马程序员订阅用户
上一篇 2023年5月12日 10:25
下一篇 2023年5月12日 10:25

相关推荐

  • 我来教你12核24线程的处理器是一个还是两个。

    在现代计算机领域,多核心处理器已经变得非常普遍,它们通过在单个芯片上集成多个独立的核心来提供更高的处理能力,从而允许并行处理多个任务,当我们提到“十二核二十四线程”,我们正在讨论一种具有12个物理核心的…

    2024年6月11日
    00
  • 分享如何检查高CPU/内存消耗进程。

    您可以使用以下命令来检查高CPU/内存消耗进程:,- top 命令可以查看最耗CPU的进程,也可以查看该进程中最耗CPU的线程。,- ps 命令可以查看进程的瞬间信息,包括 CPU 占用率、内存使用量等。 如何检查高CPU/内存消…

    2024年7月6日
    00
  • 我来教你c#中多线程。

    C#中多线程是实现并行处理的一种方式,可以提高程序的执行效率。 在C中,多线程是一种处理多个数据的有效方法,通过使用多线程,我们可以同时执行多个任务,从而提高程序的执行效率,本文将详细介绍如何在C中使用多…

    2024年7月12日
    00
  • 我来说说storm崩溃问题怎么解决。

    Storm是一个开源的分布式实时计算系统,被广泛应用于大数据处理、实时分析等领域,在使用过程中,可能会遇到Storm崩溃的问题,本文将介绍一些常见的Storm崩溃问题及其解决方法。 1. 内存不足导致崩溃 Storm在运行过…

    2024年6月13日
    00
  • 说说七彩虹b365m主板配什么显卡。

    七彩虹B365M主板是一款基于Intel B365芯片组的微型ATX主板,适用于搭建入门级至中等性能的桌面电脑,在选择CPU时,需要确保所选处理器与主板兼容,同时符合用户的性能需求和预算,以下是针对七彩虹B365M主板搭配CPU…

    2024年6月21日
    04
  • java关闭线程的方法有哪些。

    Java关闭线程的方法有:使用标志位、使用interrupt()方法、使用Thread.stop()方法。 在Java中,线程是程序执行的最小单位,我们需要关闭一个线程以停止其执行,Java提供了多种方法来关闭线程,下面我们将详细介绍这…

    2024年7月11日
    00
  • 小编教你vc多线程编译怎么实现程序运行。

    VC多线程编译实现程序运行,需要在项目设置中启用多线程支持,并使用相关API进行线程创建、同步和互斥操作。 VC多线程编译怎么实现 在编程中,多线程编程是一种常见的技术,它可以提高程序的执行效率,Visual C++(V…

    2024年7月7日
    00
  • 说说如何实现linux多线程编程。

    在Linux环境下,多线程编程是一种常见的并发编程方式,它允许程序同时执行多个任务,从而提高了程序的执行效率,本文将详细介绍如何在Linux环境下实现多线程编程。 线程的基本概念 线程是操作系统能够进行运算调度…

    2024年7月11日
    00

联系我们

QQ:951076433

在线咨询:点击这里给我发消息邮件:951076433@qq.com工作时间:周一至周五,9:30-18:30,节假日休息