- 背景
最近看到项目里面有通过spring @Async声明式的方式来进行异步调用,第一时间就让我警惕起来,原因在于通常情况下我们都是通过JDK的ThreadPoolExecutor来做这个事儿,ThreadPoolExecutor也会有相关参数需要优化的地方,例如线程池的配置,阻塞队列的大小(可能会被OOM)等等,而通过注解异步执行的背后一定会有一个threadpool。 尽管声明式的使用方式使得程序变的更加简单,但一定得深挖一下。
- 基本用法
通过在由@Configuration注解的类上配置@EnableAsync来开启spring异步方法执行的能力
@Configuration
@EnableAsync
public class MyJob {
@Async
public CompletableFuture<String> doSomething() {
return CompletableFuture.supplyAsync(this::doJob);
}
private String doJob() {
System.out.println("start the work...");
System.out.println("sleep 5 seconds to simulate the work");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end the work...");
return "done";
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringTest {
@Resource
private MyJob myJob;
private CountDownLatch countDownLatch = new CountDownLatch(1);
@Test
public void testSpringAsync() {
CompletableFuture<String> completableFuture= myJob.doSomething();
completableFuture.whenComplete((s, throwable) -> {
System.out.println("this is the result: " + s);
countDownLatch.countDown();
});
System.out.println("wait the work done!");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
result:
wait the work done!
start the work...
sleep 5 seconds to simulate the work
end the work...
this is the result: done
可以看到doSomething方法的执行异步了。
- 追踪
- 通过@EnableAsync来开启异步方法执行的能力
重点在导入AsyncConfigurationSelector来对异步配置进行选择
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
AdviceMode mode() default AdviceMode.PROXY;
...
}
- 由AsyncConfigurationSelector来对异步配置进行选择
AdviceMode 在@EnableAsync中默认配置为AdviceMode.PROXY,ProxyAsyncConfiguration则主要配置了AsyncAnnotationBeanPostProcessor
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
...
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;
}
}
}
@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() {
...
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
...
}
}
- AsyncAnnotationBeanPostProcessor实现了BeanFactoryAware接口,在setBeanFactory的时候会创建AsyncAnnotationAdvisor
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
}
- AsyncAnnotationAdvisor主要创建了advice和pointcut,advice环绕方法的执行,pointcut用来匹配目标方法
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
...
@SuppressWarnings("unchecked")
public AsyncAnnotationAdvisor(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
asyncAnnotationTypes.add(Async.class);
try {
asyncAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// If EJB 3.1 API not present, simply ignore.
}
this.advice = buildAdvice(executor, exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
protected Advice buildAdvice(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
/**
* 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);
}
}
- 主要看第4步中buildAdvice中的AnnotationAsyncExecutionInterceptor,AnnotationAsyncExecutionInterceptor继承了AsyncExecutionInterceptor,AsyncExecutionInterceptor继承了AsyncExecutionAspectSupport
public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor {
...
}
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
/**
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
* as of 4.2.6, a local executor for this interceptor will be built otherwise
*/
public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
super(defaultExecutor);
}
/**
* Create a new {@code AsyncExecutionInterceptor}.
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
* as of 4.2.6, a local executor for this interceptor will be built otherwise
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
*/
public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
super(defaultExecutor, exceptionHandler);
}
/**
* Intercept the given method invocation, submit the actual calling of the method to
* the correct task executor and return immediately to the caller.
* @param invocation the method to intercept and make asynchronous
* @return {@link Future} if the original method returns {@code Future}; {@code null}
* otherwise.
*/
@Override
@Nullable
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());
}
@Override
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}
}
AsyncExecutionAspectSupport主要有2点值得关注,第一是提供了Executor的默认创建方式,即通过spring容器来获取TaskExecutor Bean,第二是对异步执行方法返回值的适配
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
/**
* Create a new {@link AsyncExecutionAspectSupport} with the given exception handler.
* @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific
* executor has been requested via a qualifier on the async method, in which case the
* executor will be looked up at invocation time against the enclosing bean factory
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
*/
public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory));
this.exceptionHandler = SingletonSupplier.of(exceptionHandler);
}
/**
* Retrieve or build a default executor for this advice instance.
* An executor returned from here will be cached for further use.
* <p>The default implementation searches for a unique {@link TaskExecutor} bean
* in the context, or for an {@link Executor} bean named "taskExecutor" otherwise.
* If neither of the two is resolvable, this implementation will return {@code null}.
* @param beanFactory the BeanFactory to use for a default executor lookup
* @return the default executor, or {@code null} if none available
* @since 4.2.6
* @see #findQualifiedExecutor(BeanFactory, String)
* @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
*/
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
if (beanFactory != null) {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
return beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
logger.debug("Could not find unique TaskExecutor bean", ex);
try {
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
if (logger.isInfoEnabled()) {
logger.info("More than one TaskExecutor bean found within the context, and none is named " +
"'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +
"as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
}
}
}
catch (NoSuchBeanDefinitionException ex) {
logger.debug("Could not find default TaskExecutor bean", ex);
try {
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
logger.info("No task executor bean found for async processing: " +
"no bean of type TaskExecutor and no bean named 'taskExecutor' either");
}
// Giving up -> either using local default executor or none at all...
}
}
return null;
}
/**
* Delegate for actually executing the given task with the chosen executor.
* @param task the task to execute
* @param executor the chosen executor
* @param returnType the declared return type (potentially a {@link Future} variant)
* @return the execution result (potentially a corresponding {@link Future} handle)
*/
@Nullable
protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
if (CompletableFuture.class.isAssignableFrom(returnType)) {
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
}
catch (Throwable ex) {
throw new CompletionException(ex);
}
}, executor);
}
else if (ListenableFuture.class.isAssignableFrom(returnType)) {
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
}
else if (Future.class.isAssignableFrom(returnType)) {
return executor.submit(task);
}
else {
executor.submit(task);
return null;
}
}
}
- 追踪TaskExecutor的默认创建方式
在spring-boot-autoconfigure中可以查到如下配置类,默认情况下会创建一个coresize为8,maxsize为Integer.MAX_VALUE,队列大小为Integer.MAX_VALUE的线程池。
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {
/**
* Bean name of the application {@link TaskExecutor}.
*/
public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";
private final TaskExecutionProperties properties;
private final ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers;
private final ObjectProvider<TaskDecorator> taskDecorator;
public TaskExecutionAutoConfiguration(TaskExecutionProperties properties,
ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
ObjectProvider<TaskDecorator> taskDecorator) {
this.properties = properties;
this.taskExecutorCustomizers = taskExecutorCustomizers;
this.taskDecorator = taskDecorator;
}
@Bean
@ConditionalOnMissingBean
public TaskExecutorBuilder taskExecutorBuilder() {
TaskExecutionProperties.Pool pool = this.properties.getPool();
TaskExecutorBuilder builder = new TaskExecutorBuilder();
builder = builder.queueCapacity(pool.getQueueCapacity());
builder = builder.corePoolSize(pool.getCoreSize());
builder = builder.maxPoolSize(pool.getMaxSize());
builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
builder = builder.keepAlive(pool.getKeepAlive());
builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
builder = builder.customizers(this.taskExecutorCustomizers);
builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
return builder;
}
@Lazy
@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
}
- 结论
至此,对通过注解异步方法执行的最踪基本完成,spring 声明式的方式使得应用程序的构建变得更加简便,特别是在基础能力的封装上,例如过去对事务,缓存,定时任务等相关的应用,但重点在于我们得清楚声明式背后的逻辑。