异步执行的功能在业务场景中使用的地方不多,但是这种功能不可或缺。Spring给我们提供了很方便的使用方式,这里来解析一下这个功能。
一、使用方式
异步执行肯定要使用到线程,所以在SpringBoot中肯定有配置线程池的地方,因为所有的异步任务都会丢给线程池来执行。
事实上,Spring就是这么做的,如果你没有配置线程池,那么Spring每次在执行异步任务时,会即时新建一个线程来执行任务,如果你配置了自己的线程池,那么异步任务会交给配置的线程池来执行。在应用中有较高频率使用异步任务的情况下,最好是配置线程池,用于节省线程创建和销毁的开销。
使用异步任务功能的步骤如下:
1、在启动类上添加注解:
@EnableAsync
2、新建配置类:
@Configuration
public class SpringAsyncConfig {
@Bean(name = "taskExecutor")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(4);
taskExecutor.setMaxPoolSize(8);
taskExecutor.setQueueCapacity(20);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("taskExecutor-thread-");
// 拒绝的处理策略,默认有abortPolicy、CallerRunsPolicy;默认为后者
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 调度器shutdown被调用时等待当前被调度的任务完成
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
// 空闲存活时间
taskExecutor.setAwaitTerminationSeconds(20);
taskExecutor.initialize();
return taskExecutor;
}
}
3、在需要的方法上添加异步注解@Async:
@Async可以用于修饰类型,此时这个类里面所有的方法都是异步的。
public class A {
@Async("taskExecutor")
@Override
public void fun() {
System.out.println("1");
}
}
使用方法很简单,但是里面涉及的知识点很多。
二、异步原理
异步这里配置的线程池就不解释了,这是个基本点,必须掌握。
我们要探索的是Spring如何是如何将任务交给线程池的,我们先可以猜测一下如果是我们来实现,应该要怎样做?首先,肯定是使用AOP生成代理对象来解决,我们知道,cglib是通过动态生成一个子类来实现动态代理,这里我们认为Spring通过cglib来生成代理的,使用了@Async的类会被生成的代理类替代,所有使用该类的地方,都会被替换成一个增强的代理类对象,这个好理解。但是我们知道,给线程池的任务类需要是Runnable或者Callabel接口的实现者,代理类没理由去实现这个接口,就算是,如果一个类当中有多个@Sync修饰的方法怎么办?其实,只要搞个中间层就好了,生成的代理类封装一下@Async修饰的方法,里面的逻辑大致是:
pool.submit(new Runnable() {
@Override
public void run() {
proxy.fun();
}
});
这样是不是就可以解决了,中间层调用一下就解决了。
上面我们猜测了下大致的方案,下面来看看Spring具体处理的方式。
开关肯定是@EnableAsync注解,因为没有这个注解,异步功能不会开启,你后面写的@Async,配置的线程池都不会生效。
@EnableAsync注解引入了一个org.springframework.scheduling.annotation.AsyncConfigurationSelector类,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
@EnableAsync默认配置的是proxy模式,同时引入了
org.springframework.scheduling.annotation.AsyncConfigurationSelector
AsyncConfigurationSelector又引入了
org.springframework.scheduling.annotation.ProxyAsyncConfiguration
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
/**
* 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;
}
}
}
ProxyAsyncConfiguration注入了一个BeanPostProcessor:AsyncAnnotationBeanPostProcessor,我们需要看看这个BeanPostProcessor做了什么
@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;
}
}
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
做了什么,这里创建了一个advisor,他接收了线程池和异常处理参数,我们就知道他是关键了
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;
}
}
org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
这个类是重点,它里面的一个成员是重点中的重点
这里虽然还没看出Advice被谁使用,但是增强的方式就在这个里面
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
private Advice advice;
private Pointcut pointcut;
...
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;
}
...
}
接下来的重点是
org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
很遗憾这个类只是一个层次设计上的需要,并没有重要逻辑,所以需要到他的父类去看看
public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor {
public AnnotationAsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
super(defaultExecutor);
}
public AnnotationAsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
super(defaultExecutor, exceptionHandler);
}
@Override
@Nullable
protected String getExecutorQualifier(Method method) {
// Maintainer's note: changes made here should also be made in
// AnnotationAsyncExecutionAspect#getExecutorQualifier
Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class);
if (async == null) {
async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class);
}
return (async != null ? async.value() : null);
}
}
增强逻辑所在类
org.springframework.aop.interceptor.AsyncExecutionInterceptor
看到invoke()和doSubmit()我们已经可以猜到套路了,invoke()作为回调被调用,doSubmit()是往线程池里丢任务。
所以我们需要看的是谁在回调invoke(),这样整条链路就清晰了。
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
...
@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());
}
@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;
}
}
...
}
所以重点回到
org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor
他是
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
的基类,这里是使用Advisor的地方,可以看到Advisor被添加到ProxyFactory中去了
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
...
@Nullable
protected Advisor advisor;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
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注解的类和其他类型发生了循环依赖,Spring启动时会报错导致启动不了。假设A、B两个类互相依赖,且A中有@Async修饰的方法。
如果先初始化A,会先把原始A实例暴露到三级缓存,然后填充A的属性,发现B,就去实例化B,填充B的属性时直接使用了A的原始类型实例,最后完成A的实例话并生成了代理对象,但是检测到B引用的是原始类型A的实例,所以报个错,让用户处理。那为什么同样的场景,使用的是@Transactional注解替换@Async注解时却没有报错呢?这里就得好好看看上面提到的一个方法:添加到三级缓存的一个方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBea()
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
这里我只关注添加到三级缓存的方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference()
这里判断了一个SmartInstantiationAwareBeanPostProcessor子类与否,是的话就产生应有的动态代理类,很不幸的的是,处理@Async注解的BeanPostProcessor没有继承这个类,而处理@Transanctional注解BeanPostProcessor继承了这个聪明的PostProcessor,2个后置处理类分别为:
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}