@EnableAsync+@Async源码学习笔记之一

发布于:2025-04-20 ⋅ 阅读:(23) ⋅ 点赞:(0)

不说废话。直接从@EnableAsync这个注解入手。下面是这个注解的源码,注意看其中的注释。


package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;

/**
 * @EnableAsync 这个注解的作用是开启 spring 的异步方法执行能力。
 * 对应的 XML 配置是 <task:*> 这个命名空间。
 * <p>
 * Enables Spring's asynchronous method execution capability, similar to functionality
 * found in Spring's {@code <task:*>} XML namespace.
 * <p>
 * 使用方法如下。结合 @Configuration 注解。启用注解驱动的异步处理。
 *
 * <p>To be used together with @{@link Configuration Configuration} classes as follows,
 * enabling annotation-driven async processing for an entire Spring application context:
 * <p>
 * 注意看这个示例
 * 
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAsync
 * public class AppConfig {
 * <p>
 * }</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">
 * &#064;Configuration
 * public class AnotherAppConfig {
 *
 *     &#064;Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 * }</pre>
 *
 * 默认情况下,spring 会寻找一个关联的线程池,这个线程池要么是应用上下文中
 * 唯一的一个 org.springframework.core.task.TaskExecutor 类型的 bean
 * 要么是一个名字为 taskExecutor 的类型为 java.util.concurrent.Executor 的 bean
 * 如果上面2个都没找到的话,默认使用 SimpleAsyncTaskExecutor 去处理。
 * 另外,如果方法的返回值是 void 的话,这个方法不能向调用者传递异常,默认情况下,出异常了只会打印日志。
 *
 * <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>
 * 上面说的这些配置信息可以通过实现 AsyncConfigurer 接口来自定义。
 * 实现了 AsyncConfigurer 接口以后可以自定义任务执行器、自定义异常处理。
 *
 * <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">
 * &#064;Configuration
 * &#064;EnableAsync
 * public class AppConfig implements AsyncConfigurer {
 *
 *     &#064;Override
 *     public Executor getAsyncExecutor() {
 *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 *         executor.setCorePoolSize(7);
 *         executor.setMaxPoolSize(42);
 *         executor.setQueueCapacity(11);
 *         executor.setThreadNamePrefix("MyExecutor-");
 *         executor.initialize();
 *         return executor;
 *     }
 *
 *     &#064;Override
 *     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
 *         return MyAsyncUncaughtExceptionHandler();
 *     }
 * }</pre>
 *
 * 也可以通过继承 AsyncConfigurerSupport 来自定义。
 *
 * <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>
 * 需要注意的是,上面的例子中创建的 ThreadPoolTaskExecutor 不是一个完全被 spring 管理的 bean 。
 * 可以在 getAsyncExecutor 方法上加上 @Bean 注解来让创建的 ThreadPoolTaskExecutor 成为
 * 一个完全被 spring 管理的 bean。在这种情况下,就没有必要调用 executor.initialize() 方案了。
 *
 * <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>
 * 等价的 XML 配置如下
 *
 * <p>For reference, the example above can be compared to the following Spring XML
 * configuration:
 * <p>
 * <pre class="code">
 * {@code
 * <beans>
 * <p>
 *     <task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/>
 * <p>
 *     <task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/>
 * <p>
 *     <bean id="asyncBean" class="com.foo.MyAsyncBean"/>
 * <p>
 *     <bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/>
 * <p>
 * </beans>
 * }</pre>
 *
 * 如果使用 XML 配置的话,不能指定线程名称的前缀。
 * <p>
 * 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.
 *
 * <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.
 *
 * @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
// 关键是这个 @Import
// 关键是这个 @Import

// 这是入口点
// 这是入口点
// 这是入口点

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {

	/**
     * 默认支持 spring 的 @Async 注解和 EJB 的 @Asynchronous 注解
     * 还可以自定义
     * <p>
	 * 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;

	/**
	 * 表明是使用基于子类(CGLIB)的代理还是Java标准的基于接口的代理。
	 * 默认是使用Java默认支持的基于接口的动态代理。
	 * 只有当 mode 属性的值是 PROXY 的时候,这个 proxyTargetClass 才有用。
	 * 需要注意的是,当这个属性设置为 true 的时候,它不仅仅影响异步配置(@Async),其他的所有需要代理的bean都会受到影响。
	 * 比如,@Transactional 注解也会受到影响,变成基于子类的代理(CGLIB)。
	 *
	 * 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;

	/**
	 * 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;

	/**
	 * 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这个注解的作用是开启spring的异步方法执行能力。这个就不用解释了吧。
  • 文档中的第二段告诉你@EnableAsync这个注解怎么用,如下:
@Configuration
@EnableAsync
public class AppConfig {

}
@Configuration
public class AnotherAppConfig {

    @Bean
    public MyAsyncBean asyncBean() {
        return new MyAsyncBean();
    }
    
}
public class MyAsyncBean {

    @Async
    public void a() {
        System.out.println("可以用 spring 的 @Async 注解");
    }
    
    @javax.ejb.Asynchronous
    public void b() {
        System.out.println("还可以用 EJB 3.1 的 @javax.ejb.Asynchronous 注解");
    }

}

就像上面这样配置,就可以使用 spring 提供的异步功能了。

  • 文档的第三段就说到了底层实现中的线程池和默认的异常处理机制了。

默认情况下,spring 会寻找一个关联的线程池,这个线程池要么是应用上下文中
唯一的一个 org.springframework.core.task.TaskExecutor 类型的 bean
要么是一个名字为 taskExecutor 的类型为 java.util.concurrent.Executor 的 bean
如果上面2个都没找到的话,默认使用 SimpleAsyncTaskExecutor 去处理
而 SimpleAsyncTaskExecutor 其实不是真正意义的线程池,使用这个的话并不能实现线程复用,说白了,尽量别用它
另外,如果方法的返回值是 void 的话,这个方法不能向调用者传递异常,默认情况下,出异常了只会打印日志

  • 接着还说了,底层的这个线程池和异常处理机制都是可以自定义的,通过实现 AsyncConfigurer 接口就行,看下 AsyncConfigurer 接口的定义

package org.springframework.scheduling.annotation;

import java.util.concurrent.Executor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.lang.Nullable;

/**
 * Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
 * Configuration} classes annotated with @{@link EnableAsync} that wish to customize the
 * {@link Executor} instance used when processing async method invocations or the
 * {@link AsyncUncaughtExceptionHandler} instance used to process exception thrown from
 * async method with {@code void} return type.
 *
 * <p>Consider using {@link AsyncConfigurerSupport} providing default implementations for
 * both methods if only one element needs to be customized. Furthermore, backward compatibility
 * of this interface will be insured in case new customization options are introduced
 * in the future.
 *
 * <p>See @{@link EnableAsync} for usage examples.
 *
 * @author Chris Beams
 * @author Stephane Nicoll
 * @since 3.1
 * @see AbstractAsyncConfiguration
 * @see EnableAsync
 * @see AsyncConfigurerSupport
 */
public interface AsyncConfigurer {

	/**
	 * The {@link Executor} instance to be used when processing async
	 * method invocations.
	 */
	@Nullable
	default Executor getAsyncExecutor() {
		return null;
	}

	/**
	 * The {@link AsyncUncaughtExceptionHandler} instance to be used
	 * when an exception is thrown during an asynchronous method execution
	 * with {@code void} return type.
	 */
	@Nullable
	default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return null;
	}

}

看到了吧,就2个方法,一个返回线程池,另一个返回异常处理器,你可以通过实现这个接口并重写其中的这2个方法来进行自定义。
文档中还给你写了个例子,如下:

@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();
    }

}

上面这个例子主要演示怎样通过实现AsyncConfigurer接口并重写 getAsyncExecutor 方法来自定义线程池。
可以看到,自定义的线程池的核心线程数是7,最大线程数是42,队列容量是11,线程名称前缀是 MyExecutor-
还有一点需要注意的是,它还在最后调用了线程池的 initialize() 方法。
除了实现 AsyncConfigurer 接口,还可以继承 AsyncConfigurerSupport 类。
另外一个需要注意的点,上面这个例子中创建的 ThreadPoolTaskExecutor 不是一个 fully managed spring bean
如果你想让它变成一个 fully managed spring bean 的话,可以在 getAsyncExecutor方法上加上@Bean注解,此时,不需要手动调executor.initialize()` 方法了。

  • 文档的最后一部分是说 @EnableAsync 这个注解的几个属性的。

需要提前说明的是,@EnableAsync底层实际是用aop、动态代理这些技术实现的。而 modeproxyTargetClass 这2个属性就是用来配置代理的。
不光是 @EnableAsync 这个注解有这2个属性,spring 中很多依靠aop或者说动态代理实现的技术都会提供这两个属性。

  • mode
    • 可取值
      • AdviceMode.PROXY -----> JDK proxy-based advice
      • AdviceMode.ASPECTJ -----> AspectJ weaving-based advice
    • 默认值
      • AdviceMode.PROXY
  • proxyTargetClass
    • 只在代理模式(mode=AdviceMode.PROXY)下生效
    • 值为 true。将会创建基于类的代理。说白了就是CGLIB。
    • 值为 false(默认值)。JDK的基于接口的代理将会被创建。说白了就是JDK自带的动态代理。

还有一个属性 annotation 。这个不咋用得着,当然你闲得慌可以试试。
默认情况下 @EnableAsync 是结合 spring 的 @Async和 EJB 的 @javax.ejb.Asynchronous 来使用的。
如果你就是不想用这2个注解,想自定义一个注解,那你就可以用这个属性来指定你自定义的注解。

最后,总结下,本文讲的其实还是怎样用。

下一篇,我们将从 @EnableAsync 这个注解上定义的 @Import(AsyncConfigurationSelector.class) 入手,继续深入研究。


网站公告

今日签到

点亮在社区的每一天
去签到