目录
一、异步编程
二、@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 的使用也很简单,只需要两步即可完成:
- 在配置类中首先添加 @EnableAsync 注解来启用异步支持。
- 在需要异步执行的方法上添加 @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@Nullablepublic 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 {@Overridepublic 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博客