在上一篇中《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 <task:*>} XML namespace.
-
*
-
* <p>To be used together with @{@link Configuration Configuration} classes as follows,
-
* enabling annotation-driven async processing for an entire Spring application context:
-
*
-
* <pre class="code">
-
* @Configuration
-
* @EnableAsync
-
* public class AppConfig {
-
*
-
* }</pre>
-
*
-
* {@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>
-
*
-
* (以上部分展示了与 @Configuration 注解搭配使用的场景)
-
*
-
* <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.
-
*
-
* (默认情况下,Spring寻找一个唯一的TaskExecutor类型的bean 或者 bean名称是“taskExecutor”的Executor类型的bean。
-
* 如果二者都不存在,则使用SimpleAsyncTaskExecutor进行异步方法的执行.)
-
* 返回类型为void 无法将任何异常传送回调用方。 默认情况下,仅记录此类未捕获的异常。
-
*
-
* <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>
-
*
-
* <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 MyAsyncUncaughtExceptionHandler();
-
* }
-
* }</pre>
-
*
-
* <p>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.
-
*
-
* <p>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.
-
*
-
* <p>For reference, the example above can be compared to the following Spring XML
-
* configuration:
-
*
-
* <pre class="code">
-
* {@code
-
* <beans>
-
*
-
* <task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/>
-
*
-
* <task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/>
-
*
-
* <bean id="asyncBean" class="com.foo.MyAsyncBean"/>
-
*
-
* <bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/>
-
*
-
* </beans>
-
* }</pre>
-
*
-
* The above XML-based and JavaConfig-based examples are equivalent except for the
-
* setting of the <em>thread name prefix</em> of the {@code Executor}; this is because
-
* the {@code <task:executor>} element does not expose such an attribute. This
-
* demonstrates how the JavaConfig-based approach allows for maximum configurability
-
* through direct access to actual componentry.
-
*
-
* <p>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}(默认值),则其他
-
* 属性控制代理的行为。请注意,代理模式只允许通过代理拦截调用;同一类中的本身(自)调用不能这样被拦
-
* 截。)
-
* <p>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.
-
* <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.
-
*/
-
/**
-
* 指示要在类或方法级别检测到的“异步”注解类型。
-
* 默认情况下,Spring的@ {@Async}注解和EJB 3.1{@code @javax.ejb.Asynchronous}
-
注解将被检测到。
-
* 此属性存在,以便开发人员可以提供自己的自定义注解类型,以指示一个方法(或的所有方法给定的类)
-
* 应该异步调用。
-
*/
-
Class<? extends Annotation> annotation()
default Annotation.class;
-
-
/**
-
* 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 — for example, in tests.
-
*/
-
/**
-
* 指示与基于标准Java接口的代理相反,是否要创建基于子类(CGLIB)的代理。
-
* 仅在{@link #mode}设置为{@link AdviceMode#PROXY}时适用,默认false
-
* 请注意,将此属性设置为{@code true}将影响所有需要代理的<em> all </ em> Spring管理的
-
* bean,而不仅仅是标记为{@code @Async}的bean。 例如,其他标有Spring的{@code
-
* @Transactional}批注的bean将同时升级为子类代理。 这种方法在实践中不会产生负面影响,除非在
-
* 测试中明确期望一种代理相对于另一种代理。
-
*/
-
boolean proxyTargetClass() default false;
-
-
/**
-
* 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}.
-
*/
-
/** 指示应如何应用异步通知。
-
* <p><b>默认值是{@link AdviceMode#PROXY}</b>
-
* 请注意,代理模式只允许通过代理拦截调用。同一个类中的方法自调用不能以这种方式被拦截;本地自调用中
-
* 此类方法上的{@link Async}注解将被忽略,因为Spring的拦截器甚至不启动此类运行时场景。对于
-
* 更高级的拦截模式,请考虑将其切换到{@link AdviceMode#ASPECTJ}。
-
*/
-
AdviceMode mode() default AdviceMode.PROXY;
-
-
/**
-
* 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.
-
*/
-
/**
-
* 指示应用{@link AsyncAnnotationBeanPostProcessor}的顺序。
-
* 默认值是{@link Ordered#LOWEST_PRECEDENCE},以便在所有其他后处理器之后运行,这样它就可
-
* 以向现有代理添加一个advisor,而不是双重代理。
-
*/
-
int order() default Ordered.LOWEST_PRECEDENCE;
-
-
}
@EnableAsync 注解中有一行重要的代码:@Import(AsyncConfigurationSelector.class),引入了相关的配置类:
-
public
class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
-
-
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<? extends Annotation> 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.<Integer>getNumber(
"order"));
-
return bpp;
-
}
-
-
}
AsyncAnnotationBeanPostProcessor类:
看名称有点像bean后置处理器,继承实现接口比较复杂,可以用IDEA生成一下类图:
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的结构帮助理解:
可以看到这个 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<Class<? extends Annotation>> asyncAnnotationTypes) {
-
ComposablePointcut result =
null;
-
for (Class<? extends Annotation> 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<Object> 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
转载:https://blog.csdn.net/qq_22076345/article/details/82229447