@EnableAsync 详解,@Async 如何生效

2023-05-16

目录

  • 注释
  • 源码
    • 注解源码
    • 实现源码
  • 总结

应用见
SpringBoot:详解@EnableAsync + @Async 实现共享线程池

学习注解,从注释和源码入手

注释

部分关键注释,我自己标注了一些中文注释便于理解:

  1. 作用:启用Spring的异步方法执行功能
/**
 * Enables Spring's asynchronous method execution capability, similar to functionality
 * found in Spring's {@code <task:*>} XML namespace.
 * <p>
 * -- 与@Configuration注解配合使用,在Spring应用程序上下文中实现注解驱动的异步处理功能
 * <p>To be used together with @{@link Configuration Configuration} classes as follows,
 * enabling annotation-driven async processing for an entire Spring application context:
 * <p>
  1. 配置类中使用方式如下
 * <pre class="code">
 * @Configuration
 * @EnableAsync
 * public class AppConfig {
 *
 * }</pre>
 * <p>
  1. 开发人员可以在方法上标注@Async注解,使得该方法加入线程池运行
 * {@code MyAsyncBean} is a user-defined type with one or more methods annotated with
 * either Spring's {@code @Async} annotation, the EJB 3.1 {@code @javax.ejb.Asynchronous}
 * annotation, or any custom annotation specified via the {@link #annotation} attribute.
 * The aspect is added transparently for any registered bean, for instance via this
 * configuration:
 *
 * <pre class="code">
 * @Configuration
 * public class AnotherAppConfig {
 *
 *     @;Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 * }</pre>
 * <p>
  1. 默认情况下,Spring搜索一个线程池定义
 * <p>By default, Spring will be searching for an associated thread pool definition:
 * either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,
 * or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. If
 * neither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}
 * will be used to process async method invocations. Besides, annotated methods having a
 * {@code void} return type cannot transmit any exception back to the caller. By default,
 * such uncaught exceptions are only logged.
 * <p>
  1. 开发者要实现AsyncConfigurer接口,自己提供线程池和异常处理器
    实际开发中也可以不实现此接口,在配置类中的方法(如命名方法为asyncServiceExecutor)加上@Bean注解,在调用的方法上使用@Async(“asyncServiceExecutor”),来指定运行此方法的线程池
 * <p>To customize all this, implement {@link AsyncConfigurer} and provide:
 * <ul>
 * <li>your own {@link java.util.concurrent.Executor Executor} through the
 * {@link AsyncConfigurer#getAsyncExecutor getAsyncExecutor()} method, and</li>
 * <li>your own {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler
 * AsyncUncaughtExceptionHandler} through the {@link AsyncConfigurer#getAsyncUncaughtExceptionHandler
 * getAsyncUncaughtExceptionHandler()}
 * method.</li>
 * </ul>
  1. 讲了依赖与延迟加载
 * <p><b>NOTE: {@link AsyncConfigurer} configuration classes get initialized early
 * in the application context bootstrap. If you need any dependencies on other beans
 * there, make sure to declare them 'lazy' as far as possible in order to let them
 * go through other post-processors as well.</b>
 * <p>
  1. 代码示例
 * <pre class="code">
 * @Configuration
 * @EnableAsync
 * public class AppConfig implements AsyncConfigurer {
 *
 *     @Override
 *     public Executor getAsyncExecutor() {
 *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 *         executor.setCorePoolSize(7);
 *         executor.setMaxPoolSize(42);
 *         executor.setQueueCapacity(11);
 *         executor.setThreadNamePrefix("MyExecutor-");
 *         executor.initialize();
 *         return executor;
 *     }
 *
 *     @Override
 *     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
 *         return new MyAsyncUncaughtExceptionHandler();
 *     }
 * }</pre>
 */

源码

注解源码

  1. 注解自身还有四个注解,包括三个基本注解和一个导入注解
    @Target(ElementType.TYPE) 表示可修饰的范围:接口、类、枚举
    @Retention(RetentionPolicy.RUNTIME)表示保留的时间范围:运行时
    @Documented表示可以配合javadoc
    @Import(AsyncConfigurationSelector.class)导入了AsyncConfigurationSelector类,与mode()方法有关
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
  1. 监测 @async 注解的方法
	/**
	 * Indicate the 'async' annotation type to be detected at either class
	 * or method level.
	 * <p>By default, both Spring's @{@link Async} annotation and the EJB 3.1
	 * {@code @javax.ejb.Asynchronous} annotation will be detected.
	 * <p>This attribute exists so that developers can provide their own
	 * custom annotation type to indicate that a method (or all methods of
	 * a given class) should be invoked asynchronously.
	 */
	Class<? extends Annotation> annotation() default Annotation.class;
  1. 代理方式设置,默认false 只针对@Async注解的方法,如果true将在所有Spring管理的bean上生效
	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies.
	 * <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>.
	 * <p>The default is {@code false}.
	 * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
	 * Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
	 * For example, other beans marked with Spring's {@code @Transactional} annotation
	 * will be upgraded to subclass proxying at the same time. This approach has no
	 * negative impact in practice unless one is explicitly expecting one type of proxy
	 * vs. another &mdash; for example, in tests.
	 */
	boolean proxyTargetClass() default false;
  1. 指定异步能生效的模式,默认为Porxy(JDK proxy-based advice.),另一种是ASPECTJ(AspectJ weaving-based advice)
	/**
	 * Indicate how async advice should be applied.
	 * <p><b>The default is {@link AdviceMode#PROXY}.</b>
	 * Please note that proxy mode allows for interception of calls through the proxy
	 * only. Local calls within the same class cannot get intercepted that way; an
	 * {@link Async} annotation on such a method within a local call will be ignored
	 * since Spring's interceptor does not even kick in for such a runtime scenario.
	 * For a more advanced mode of interception, consider switching this to
	 * {@link AdviceMode#ASPECTJ}.
	 */
	AdviceMode mode() default AdviceMode.PROXY;
  1. 处理的优先级顺序
	/**
	 * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor}
	 * should be applied.
	 * <p>The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run
	 * after all other post-processors, so that it can add an advisor to
	 * existing proxies rather than double-proxy.
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;

实现源码

点进@EnableAsync的@Import(AsyncConfigurationSelector.class)
AsyncConfigurationSelector中,selectImports方法在spring容器扫描bean的时候,根据通知模型判断要导入哪一个类

	/**
	 * Returns {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration}
	 * for {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()},
	 * respectively.
	 */
	@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;
		}
	}

默认是proxy
在这里插入图片描述
返回的类为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;
	}

}

这个类是一个配置类,实现了BeanPostProcessor接口,它向spring容器中添加了一个AsyncAnnotationBeanPostProcessor的bean后置处理器,其父类AbstractAdvisingBeanPostProcessor中定义了处理器

 @Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.advisor == null || bean instanceof AopInfrastructureBean) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}

		// 类中加@Async的bean会走到这个if里,添加advisor
		if (bean instanceof Advised) {
			Advised advised = (Advised) bean;
			if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
				// Add our local Advisor to the existing proxy's Advisor chain...
				if (this.beforeExistingAdvisors) {
					advised.addAdvisor(0, this.advisor);
				}
				else {
					advised.addAdvisor(this.advisor);
				}
				return bean;
			}
		}

		if (isEligible(bean, beanName)) {
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			customizeProxyFactory(proxyFactory);
			return proxyFactory.getProxy(getProxyClassLoader());
		}

		// No proxy needed.
		return bean;
	}

可以看出前置处理器未做处理,后置处理器对添加了@Async的bean进行了addAdvisor,后续使用bean时,debug看一下,该bean中会有响应的advisor
在这里插入图片描述

总结

@EnableAsync通过向Spring引入后置处理器AsyncAnnotationBeanPostProcessor,在bean的创建过程中对bean进行advisor增强,对@Async标识的bean增强异步功能

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@EnableAsync 详解,@Async 如何生效 的相关文章

  • 脱敏工具类。手机号、银行卡号、身份证号、关键信息脱敏

    手机号 银行卡号 身份证号 关键信息脱敏 老规矩 xff0c 话不多说直接上代码 span class token keyword package span span class token namespace com span class
  • vue3.x+ts项目创建,配置流程

    一 创建项目 安装组件库 1 创建vue3 x项目 npm init vue 64 latest 2 项目中安装 element plus NPM npm install element plus save Yarn yarn add el
  • mybatis-plus条件构造器以及QueryWrapper用法学习

    QueryWrapper的基本用法 话不多说 xff0c 直接上代码 更多使用方法传送门 span class token keyword public span span class token keyword void span spa
  • linux下安装aMule

    linux下安装aMule 在Linux下安装aMule xff0c 亲测有效 xff0c 话不多说 xff0c 直接上命令 cd span class token operator span opt span class token op
  • idea中debug启动项目特别慢的解决方法

    问题描述 问题 xff1a idea中使用debug方式启动项目 xff0c 原本30s不到的启动时间 xff0c 现在启动的时间超过了3分钟甚至更久 我自己的问题以及解决方案 xff0c 你们也有可能是其他原因 我本人的问题是因为打了太多
  • MacBook安装jdk1.8方便快捷稳定的方法

    MacBook安装jdk方便快捷稳定的方法 最简单快捷的jdk1 8安装 先查看有没有安装jdk xff1a java version 这样就是没有安装 然后去官网下载 下载地址 xff1a https www java com zh CN
  • RubyMine创建第一个ruby项目并运行

    第一步 xff1a 创建一个项目 RubyMine已分享网盘 xff1a 链接 https pan baidu com s 1MTbjYmuS1SxrgR1wBAmsoA pwd 61 5dh3 提取码 5dh3 Location xff1
  • 创建一个rails入门项目并运行

    创建第一个rails项目 从java转到ruby xff0c 正在学习中 xff0c 创建第一个rails项目 xff0c 做一些记录 首先要检查一下前置条件 检查是否安装ruby xff1a ruby version 我这里是已经安装了
  • rails/ruby字符串与数组之间的转换

    rails ruby字符串与数组之间的转换 初步接触ruby xff0c 点点滴滴记录下来 xff0c 希望能帮助同样是新加入ruby的同学 字符串与数组之间的转换 span class token comment ruby字符串转数组 s
  • this is incompatible with sql_mode=only_full_group_by解决方案

    MySQL查询时报错 xff1a this is incompatible with sql mode 61 only full group by 报错原因分析 xff1a 一 原理层面 这个错误发生在mysql 5 7 5 版本及以上版本
  • macbook安装Redis客户端another-redis-desktop-manager

    macbook安装Redis客户端another redis desktop manager 不讲废话 xff0c 直接上命令 macbook下使用brew安装another redis desktop manager brew insta
  • ruby on rails读取excel文件完整流程

    前端上传excel文件 xff0c 后端读取文件并写入数据库 话不多说 xff0c 直接上代码 代码复制可直接使用 xff0c 经过多次测试验证 Gemfile文件中引入 xff1a gem span class token string
  • vue3 + vite + ts + setup , 第二十一练 vue3 中使用函数式编程,h函数(一)

    之前跟大家介绍了两种vue编写风格分别是template模板方式 xff0c 和JSX方式感觉JSX被大家吐槽的很厉害 xff0c 其实用习惯还挺好用的今天介绍第三种函数式编程 主要会用到h函数 h 函数是一个用于创建 VNode 的实用程
  • 线程安全与实现方法

    线程安全 文章目录 线程安全线程安全的定义线程安全的分类补充 xff1a this引用逃逸什么是this引用逃逸逃逸场景场景一场景二 解决方案 线程安全的实现互斥同步 xff08 阻塞同步 xff09 实现举例 非阻塞同步实现举例 无同步方
  • github api 连接出错的几种情况和解决办法

    网络连接正常 xff0c github也能连接 xff0c 但是idea terminal 将代码push到远端时无法与github获取连接 情况一 使用git pus命令报错 fatal HttpRequestException enco
  • 【Proteus仿真】| 05——问题记录

    系列文章目录 Proteus仿真 01 软件安装 Proteus仿真 02 基础使用 Proteus仿真 03 超详细使用教程 Proteus仿真 04 绘制原理图模板 Proteus仿真 05 问题记录 文章目录 前言1 51单片机仿真2
  • 在Winxp虚拟机上安装Vxworks虚拟机,完成VxWorks_Tornado开发环境搭建

    前言 关于在虚拟机上安装虚拟机的做法并不普遍 xff0c 因为其实用性并不大 xff0c 对于很多人来说 xff0c 并没有必要再虚拟机上安装虚拟机 xff0c 以来这种做法确实很浪费内存资源 xff0c 二来没有具体的实用价值 因为主机装
  • centos7 搭建httpd服务

    查看是否有httpd root 64 Cheney yum list httpd 已加载插件 xff1a fastestmirror langpacks Loading mirror speeds from cached hostfile
  • 邮件系统被退回的原因及解决办法

    邮件为什么会被退回经常上网发送邮件的人可能会有邮件被退回的经历 xff0c 收到被退回的邮件要具体分析 xff0c 退回的信件一般都会有简短的说明 xff0c 结合这些说明你可以进一步了解具体的退信原因并作出相应处理 一 退信由哪些内容组成
  • Java lambda List,树数据处理工具类

    前言 再日常的业务开发中 xff0c 常少不了list for循环处理 在查询mysql业务中 xff0c 通常不会在for循环中去查询 xff0c 而是一次去查询符合条件的数据 xff0c 以避免频繁的释放和获取连接 xff0c 然后转成

随机推荐