搜索
简帛阁>技术文章>@EnableAsync 详解,@Async 如何生效

@EnableAsync 详解,@Async 如何生效

目录

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

应用见
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增强异步功能

官方文档:@EnableAsyncThe@Asyncannotation官方案例:https://springio/guides/gs/asyncmethod一、在spring中使用异步处理1如何使用
@EnableAsync、@Async注解使用启动类:importorgspringframeworkbootSpringApplication;importorgspringframeworkboo
1、本文内容详解@EnableAsync@Async,主要分下面几个点进行介绍。作用用法获取异步执行结果自定义异步执行的线程池自定义异常处理线程隔离源码原理2、作用spring容器中实现bean方法的
今天发现了一个奇怪的问题,@Async在某些地方不生效,不是异步的而是同步。(自己对SpringBoot的@Async注解不是很了解导致的)大概代码如下:publicvoidhandData()th
EnableAsync注解的意思是可以异步执行,就是开启多线程的意思。可以标注在方法、类上。@ComponentpublicclassTask{@AsyncpublicvoiddoTaskOne()
前言小程序本身是不支持async/await语法的,但有些应用场景,我们使用async/await会使得代码更简洁,也更易于维护,用过都知道是有多爽的。既然小程序不支持,那我们可以借助fackbook
随着Nodejsv8的发布,Nodejs已原生支持async/await函数,Web框架Koa也随之发布了Koa2正式版,支持async/await中间件,为处理异步回调带来了极大的方便。既然Koa
目录jQuery的$ajaxWebpack时代的开始深入了解Promise消灭嵌套awaittojs总结jQuery的$ajax在开始之前我们先来聊聊我的js异步之路。在我还在学校的时候,那时候还是j
async语法:asyncfunctionname(param){<!>statements}其中name是函数名称,param是要传递给函数的参数,statements是函数体返回值a
目录问题描述:解决方案:总结:SpringBoot注解@Async生效的解决方法问题描述:这里虽然加了@EnableAsync和@Async,但是异步请求依然没有生效解决方案:方法一:同一个类中调用