深入浅出 Spring @Async 异步编程的艺术

目录

一、异步编程

二、@Async 介绍

2.1 @Async 使用

三、@Async 原理


一、异步编程

        在软件开发中,异步编程是非常关键的,尤其是构建高性能、高响应度的应用时。异步编程的主要优势在于它能够避免阻塞操作,提高程序的效率和用户体验。异步编程的应用场景:

  • 在 Web 应用中,服务器可以处理多个请求,而无需等待耗时的操作完成,将耗时的操作进行异步处理。
  • 高并发场景中,异步模型可以处理大量并发连接,而不会因为创建过多线程而导致系统资源耗尽。
  • 使用消息队列进行异步处理,避免影响服务的性能。

        而在 Spring 框架中,提供了 @Async 注解,一行代码就帮我们搞定了异步调用。Async 注解用起来简单高效,但是如果不对其底层实现做深入研究,难免有时候也会心生疑虑,甚至会因使用不当,遇见一些让人摸不着头脑的问题。

        下面就来一起探讨下 @Async 注解。

二、@Async 介绍

        @Async 是 Spring 框架中的一个注解,用于支持异步方法的执行。在 Spring 中,当你在一个类的方法上使用 @Async 注解时,Spring 会确保该方法在不同的线程中执行,而不是调用者所在的线程。这意味着方法的执行可以并行进行,从而提高了应用程序的性能,尤其是在处理耗时的任务时。

        下面是 @Async 注解的源码,很简单,只有一个 value 属性,从源码上可以看出,这个注解可以应用在方法上,也可以应用在类上。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {

	/**
	 * A qualifier value for the specified asynchronous operation(s).
	 * <p>May be used to determine the target executor to be used when executing
	 * the asynchronous operation(s), matching the qualifier value (or the bean
	 * name) of a specific {@link java.util.concurrent.Executor Executor} or
	 * {@link org.springframework.core.task.TaskExecutor TaskExecutor}
	 * bean definition.
	 * <p>When specified on a class-level {@code @Async} annotation, indicates that the
	 * given executor should be used for all methods within the class. Method-level use
	 * of {@code Async#value} always overrides any value set at the class level.
	 * @since 3.1.2
	 */
	String value() default "";

}

2.1 @Async 使用

        @Async 的使用也很简单,只需要两步即可完成:

  1.  在配置类中首先添加 @EnableAsync 注解来启用异步支持。
  2. 在需要异步执行的方法上添加 @Async 注解。

        通过以上两步就可以使用异步任务执行了,默认情况下,Spring 使用一个简单的线程池来执行异步任务,但是可以再 @Async 注解中指定一个参数来使用自定义的线程池,例如:

@Async("myThreadPoolTaskExecutor")
public void task() {
    // Method implementation...
}

三、@Async 原理

         添加 @EnableAsync 注解后,服务启动时会触发一系列的配置和组件注册。@EnableAsync 部分源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {

}

           通过 @Import 导入了 AsyncConfigrationSelector,AsyncConfigrationSelector 会根据配置模式创建一个 AsyncConfigure,有两种配置模式,分别创建两种不同的 AsyncConfigure。

  • PROXY:默认模式,使用 JDK 动态代理或 CGLIB 代理(取决于 proxyTargetClass 属性)。在这种模式下,AsyncConfigurationSelector 将会选择导入 ProxyAsyncConfiguration类,意味着将使用代理来拦截和处理异步方法调用。
  • ASPECTJ:如果应用程序使用了 AspectJ,在这种模式下,AsyncConfigurationSelector 选择导入一个与 AspectJ 相关的配置类。

        具体源码如下:

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.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 类中做了哪些工作

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
		bpp.configure(this.executor, this.exceptionHandler);
		Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
		if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
			bpp.setAsyncAnnotationType(customAsyncAnnotation);
		}
		bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
		bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
		return bpp;
	}

}

        这个类的核心方法是创建 AsyncAnnotationBeanPostProcessor 实例,这是处理 @Async 注解的关键组件。它负责解析 @Async 注解,并将异步方法调用转换成异步执行。它是 Spring AOP 的一部分,通过实现 BeanPostProcessor 接口和 Advisor 接口,AsyncAnnotationBeanPostProcessor 可以在 Spring 容器初始化期间对 Bean 进行后处理,以及定义切面(Aspect)来拦截异步方法的调用。     

          AsyncAnnotationBeanPostProcessor 会创建一个 AsyncAnnotationAdvisor,这时一个切面,用于拦截带有 @Async 注解的方法。

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {

	
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		super.setBeanFactory(beanFactory);

		AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
		if (this.asyncAnnotationType != null) {
			advisor.setAsyncAnnotationType(this.asyncAnnotationType);
		}
		advisor.setBeanFactory(beanFactory);
		this.advisor = advisor;
	}

}

        AsyncAnnotationAdvisor 中包含了 Advice,即 AnnotationAsyncExecutionInterceptor

        当方法执行时会真正创建该方法的一个代理,这样当方法被调用时,就会被 AnnotationAsyncExecutionInterceptor 拦截,然后委托给线程池进行异步处理。

        总之,通过 @EnableAsync 配置和激活异步执行所需的基础设施,包括线程池、异常处理器、AOP 切面和动态代理,使得开发者能够在不需要关心底层细节的情况下编写异步方法。

往期经典推荐

高性能的秘密:揭秘Nginx如何支撑亿级流量网站_亿级流量nginx架构-CSDN博客

Sentinel与Nacos强强联合,构建微服务稳定性基石的重要实践_sentinel 整合nacos-CSDN博客

高并发架构设计模板-CSDN博客

TiDB高手进阶:揭秘自增ID热点现象与高级调优技巧_tidb 分布式自增id-CSDN博客

SpringBoot开箱即用魔法:深度解析与实践自定义Starter-CSDN博客

Spring循环依赖的成因与破局_spring为什么会有循环依赖的需求-CSDN博客

相关推荐

  1. 深入浅出Python爬虫:掌握数据抓取艺术

    2024-07-11 16:26:09       22 阅读
  2. Promise-以往异步编程模式

    2024-07-11 16:26:09       32 阅读
  3. Qt/C++中异步编程

    2024-07-11 16:26:09       35 阅读
  4. 【Linux】 Vim:掌握高效编辑艺术

    2024-07-11 16:26:09       26 阅读
  5. 设计模式全览:编程艺术精髓!

    2024-07-11 16:26:09       44 阅读
  6. 探索Python与C/C++混合编程艺术

    2024-07-11 16:26:09       51 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 16:26:09       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 16:26:09       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 16:26:09       57 阅读
  4. Python语言-面向对象

    2024-07-11 16:26:09       68 阅读

热门阅读

  1. 使用Python进行自然语言处理

    2024-07-11 16:26:09       21 阅读
  2. 前端项目笔记&经验-001

    2024-07-11 16:26:09       22 阅读
  3. Spring SimpleAsyncTaskExecutor学习

    2024-07-11 16:26:09       19 阅读
  4. PostgreSQL的pg_bulkload工具

    2024-07-11 16:26:09       23 阅读
  5. python积的最大分解

    2024-07-11 16:26:09       22 阅读
  6. 遇到NotOfficeXmlFileException

    2024-07-11 16:26:09       21 阅读