Spring @Async 注解的使用以及原理(二)

       在上一篇中《Spring @Async 注解的使用以及原理(一)》简单介绍了@Async的使用,本篇简单分析一下原理,源码版本:spring-context-5.0.5.RELEASE.

@EnableAsync注解:

/**
 * Enables Spring's asynchronous method execution capability, similar to functionality
 * found in Spring's {@code } XML namespace.
 *
 * 

To be used together with @{@link Configuration Configuration} classes as follows, * enabling annotation-driven async processing for an entire Spring application context: * *

 * @Configuration
 * @EnableAsync
 * public class AppConfig {
 *
 * }
* * {@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: * *
 * @Configuration
 * public class AnotherAppConfig {
 *
 *     @Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 * }
* * (以上部分展示了与 @Configuration 注解搭配使用的场景) * *

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. * * (默认情况下,Spring寻找一个唯一的TaskExecutor类型的bean 或者 bean名称是“taskExecutor”的Executor类型的bean。 * 如果二者都不存在,则使用SimpleAsyncTaskExecutor进行异步方法的执行.) * 返回类型为void 无法将任何异常传送回调用方。 默认情况下,仅记录此类未捕获的异常。 * *

To customize all this, implement {@link AsyncConfigurer} and provide: *

    *
  • your own {@link java.util.concurrent.Executor Executor} through the * {@link AsyncConfigurer#getAsyncExecutor getAsyncExecutor()} method, and
  • *
  • your own {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler * AsyncUncaughtExceptionHandler} through the {@link AsyncConfigurer#getAsyncUncaughtExceptionHandler * getAsyncUncaughtExceptionHandler()} * method.
  • *
* *
 * @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 MyAsyncUncaughtExceptionHandler();
 *     }
 * }
* *

If only one item needs to be customized, {@code null} can be returned to * keep the default settings. Consider also extending from {@link AsyncConfigurerSupport} * when possible. * *

Note: In the above example the {@code ThreadPoolTaskExecutor} is not a fully managed * Spring bean. Add the {@code @Bean} annotation to the {@code getAsyncExecutor()} method * if you want a fully managed bean. In such circumstances it is no longer necessary to * manually call the {@code executor.initialize()} method as this will be invoked * automatically when the bean is initialized. * *

For reference, the example above can be compared to the following Spring XML * configuration: * *

 * {@code
 * 
 *
 *     
 *
 *     
 *
 *     
 *
 *     
 *
 * 
 * }
* * The above XML-based and JavaConfig-based examples are equivalent except for the * setting of the thread name prefix of the {@code Executor}; this is because * the {@code } element does not expose such an attribute. This * demonstrates how the JavaConfig-based approach allows for maximum configurability * through direct access to actual componentry. * *

The {@link #mode} attribute controls how advice is applied: If the mode is * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior * of the proxying. 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. * *({@link#mode}属性控制通知的应用方式:如果模式是{@link AdviceMode#PROXY}(默认值),则其他 * 属性控制代理的行为。请注意,代理模式只允许通过代理拦截调用;同一类中的本身(自)调用不能这样被拦 * 截。) *

Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in * this case the {@code spring-aspects} module JAR must be present on the classpath, with * compile-time weaving or load-time weaving applying the aspect to the affected classes. * There is no proxy involved in such a scenario; local calls will be intercepted as well. *(注意,如果{@linkplain#mode}设置为{@link AdviceMode#ASPECTJ},则 * {@link#proxyTargetClass}属性的值将被忽略。还要注意,在这种情况下{@code spring aspects} * 模块JAR必须出现在类路径上,编译时编织或加载时编织将方面应用于受影响的类。在这种情况下不涉及代 * 理;本地调用也将被拦截。) * * @author Chris Beams * @author Juergen Hoeller * @author Stephane Nicoll * @author Sam Brannen * @since 3.1 * @see Async * @see AsyncConfigurer * @see AsyncConfigurationSelector */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class)//注意这个,重点 public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at either class * or method level. *

By default, both Spring's @{@link Async} annotation and the EJB 3.1 * {@code @javax.ejb.Asynchronous} annotation will be detected. *

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. */ /** * 指示要在类或方法级别检测到的“异步”注解类型。 * 默认情况下,Spring的@ {@Async}注解和EJB 3.1{@code @javax.ejb.Asynchronous} 注解将被检测到。   * 此属性存在,以便开发人员可以提供自己的自定义注解类型,以指示一个方法(或的所有方法给定的类) * 应该异步调用。 */ Class annotation() default Annotation.class; /** * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. *

Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}. *

The default is {@code false}. *

Note that setting this attribute to {@code true} will affect all * 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 — for example, in tests. */ /** * 指示与基于标准Java接口的代理相反,是否要创建基于子类(CGLIB)的代理。 * 仅在{@link #mode}设置为{@link AdviceMode#PROXY}时适用,默认false * 请注意,将此属性设置为{@code true}将影响所有需要代理的 all Spring管理的 * bean,而不仅仅是标记为{@code @Async}的bean。 例如,其他标有Spring的{@code * @Transactional}批注的bean将同时升级为子类代理。 这种方法在实践中不会产生负面影响,除非在 * 测试中明确期望一种代理相对于另一种代理。 */ boolean proxyTargetClass() default false; /** * Indicate how async advice should be applied. *

The default is {@link AdviceMode#PROXY}. * 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}. */ /** 指示应如何应用异步通知。 *

默认值是{@link AdviceMode#PROXY} * 请注意,代理模式只允许通过代理拦截调用。同一个类中的方法自调用不能以这种方式被拦截;本地自调用中 * 此类方法上的{@link Async}注解将被忽略,因为Spring的拦截器甚至不启动此类运行时场景。对于 * 更高级的拦截模式,请考虑将其切换到{@link AdviceMode#ASPECTJ}。 */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor} * should be applied. *

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. */ /** * 指示应用{@link AsyncAnnotationBeanPostProcessor}的顺序。 * 默认值是{@link Ordered#LOWEST_PRECEDENCE},以便在所有其他后处理器之后运行,这样它就可 * 以向现有代理添加一个advisor,而不是双重代理。 */ int order() default Ordered.LOWEST_PRECEDENCE; }

    @EnableAsync 注解中有一行重要的代码:@Import(AsyncConfigurationSelector.class),引入了相关的配置类:

public class AsyncConfigurationSelector extends AdviceModeImportSelector {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

	/**
	 * {@inheritDoc}
	 * @return {@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;
		}
	}

}

ProxyAsyncConfiguration类:

     以AdviceMode.PROXY为例,进入到ProxyAsyncConfiguration类中:

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类型的bean
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
		Class customAsyncAnnotation = this.enableAsync.getClass("annotation");
        //设置自定义的异步注解类型
		if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
			bpp.setAsyncAnnotationType(customAsyncAnnotation);
		}
        //设置执行器
		if (this.executor != null) {
			bpp.setExecutor(this.executor);
		}
        //设置异常处理器
		if (this.exceptionHandler != null) {
			bpp.setExceptionHandler(this.exceptionHandler);
		}
        //读取@EnableAsync注解的属性值,见父类的setImportMetadata方法
		bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
		bpp.setOrder(this.enableAsync.getNumber("order"));
		return bpp;
	}

}

AsyncAnnotationBeanPostProcessor类:

    看名称有点像bean后置处理器,继承实现接口比较复杂,可以用IDEA生成一下类图:

Spring @Async 注解的使用以及原理(二)_第1张图片

     AsyncAnnotationBeanPostProcessor类的代码比较多,需要清楚Spring bean的生命周期初始化过程,比如BeanFactoryAware、BeanPostProcessor等的执行顺序,另外还要对Spring AOP APIs(编程式创建 @AspectJ 代理)有所了解。

     在AbstractAdvisingBeanPostProcessor类中的postProcessAfterInitialization方法会会生成目标类的代理类

public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.advisor == null || bean instanceof AopInfrastructureBean) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}
        // 添加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;
			}
		}
        //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;
	}

 

     这里展示一下实例化的AsyncAnnotationBeanPostProcessor类对应的bean的结构帮助理解:

Spring @Async 注解的使用以及原理(二)_第2张图片

     可以看到这个 bean中持有一个AsyncAnnotationAdvisor类的对象advisor:buildAdvice()方法生成通知,buildPointcut生成切点。

    protected Advice buildAdvice(@Nullable Executor executor, 
     AsyncUncaughtExceptionHandler exceptionHandler) {
		return new AnnotationAsyncExecutionInterceptor(executor, exceptionHandler);
	}

	/**
	 * Calculate a pointcut for the given async annotation types, if any.
	 * @param asyncAnnotationTypes the async annotation types to introspect
	 * @return the applicable Pointcut object, or {@code null} if none
	 */
	protected Pointcut buildPointcut(Set> asyncAnnotationTypes) {
		ComposablePointcut result = null;
		for (Class asyncAnnotationType : asyncAnnotationTypes) {
			Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
			Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
			if (result == null) {
				result = new ComposablePointcut(cpc);
			}
			else {
				result.union(cpc);
			}
			result = result.union(mpc);
		}
		return (result != null ? result : Pointcut.TRUE);
	}

AsyncAnnotationAdvisor中的buildAdvice()方法,生成了AnnotationAsyncExecutionInterceptor对象,它的父类AsyncExecutionInterceptor重写了AsyncExecutionInterceptor接口的invoke方法,通过委托实现@Async异步方法的调用。在invoke()方法中获取执行器executor,创建Callable异步线程任务,提交到执行器executor(对应的线程池)中执行。

public Object invoke(final MethodInvocation invocation) throws Throwable {
		Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
		final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

		AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
		if (executor == null) {
			throw new IllegalStateException(
					"No executor specified and no default executor set on AsyncExecutionInterceptor either");
		}

		Callable task = () -> {
			try {
				Object result = invocation.proceed();
				if (result instanceof Future) {
					return ((Future) result).get();
				}
			}
			catch (ExecutionException ex) {
				handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
			}
			catch (Throwable ex) {
				handleError(ex, userDeclaredMethod, invocation.getArguments());
			}
			return null;
		};

		return doSubmit(task, executor, invocation.getMethod().getReturnType());
	} 
  

总结:

     @Async和@EnableAsync注解实现方法异步调用底层是通过AOP线程池实现的。

备注:AsyncAnnotationBeanPostProcessor类的实例化过程涉及到低级的AOP APIs有些复杂,需要对Spring bean生命周期解以及Spring AOP APIs的使用有所掌握。最好打断点,调试,感到混乱时,可以先debugger出代码块(比如方法)的结果,再进到代码中分析。

Spring bean生命周期大致流程:https://blog.csdn.net/qq_22076345/article/details/105580031

Spring AOP APIs官方文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-api

你可能感兴趣的:(Spring,Framework,spring,Async)