前言
相信很多朋友都使用过 @Async ,在某个方法上加上该注解,就会异步执行该方法。
之前一直使用 @Async ,直到最近遇到一个问题,才引发深入了解它的兴趣。
这个问题之前也遇到过多次,凭借多年的编码经验(也就不到一年,哈哈),虽然也能解决此类问题,但是本着彻底解开心中迷惑的原则,我们还是深入学习一下吧。
一个问题引发的思考
不知道大家在使用 @Async 的时候有没有遇到这么一个问题:方法内部调用,@Async 会失效
我们来看一段代码
interface: AsyncDemo
public interface AsyncDemo {
void a();
void b();
}
impl: AsyncDemoImpl
@Service
public class AsyncDemoImpl implements AsyncDemo {
@Override
public void a() {
System.out.println("开始执行a(): " + TimeUtil.getStringDate());
b();
System.out.println("执行结束a(): " + TimeUtil.getStringDate());
}
@Async
@Override
public void b() {
System.out.println("开始执行b(): " + TimeUtil.getStringDate());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行结束b(): " + TimeUtil.getStringDate());
}
}
时间工具
public class TimeUtil {
/**
* 时间工具
*
* @return 时间
*/
public static String getStringDate() {
Date currentTime = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
return formatter.format(currentTime);
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class AsyncDemoApplicationTests {
@Autowired
private AsyncDemo asyncDemo;
@Test
public void contextLoads() {
asyncDemo.a();
}
}
执行结果
通过执行结果,我们可以看到,@Async 貌似没生效,b() 方法同步执行。
这到底是为什么呢?难道Spring在坑我?
哈哈,Spring肯定不会坑我们的,不生效只能说我们自己不懂。
从源码中寻找答案
版本
Spring版本:5.1.3.RELEASE
**Springboot版本:2.1.1.RELEASE **
让我们在源码中一探究竟,看看 @Async 在Spring中是如何设计的。
我们都知道, @Async 的生效需要添加 @EnableAsync 。那我们剖析原理就从 @EnableAsync 入手:
EnableAsync
我们先来看下 EnableAsync 的定义:
@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;
}
由代码我们不难发现,Enable 通过 @Import 导入了 AsyncConfigurationSelector 。
AsyncConfigurationSelector
我们再继续看 AsyncConfigurationSelector 具体做了什么?
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:
// EnableAsync中默认配置PROXY
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
由于 EnableAsync 中 AdviceMode 默认配置为 PROXY ,所以将 ProxyAsyncConfiguration 配置类注入到容器中。
ProxyAsyncConfiguration
接下来,我们进入到 ProxyAsyncConfiguration 类中:
@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实例
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;
}
}
在 ProxyAsyncConfiguration 中,将 AsyncAnnotationBeanPostProcessor 类的实例注入到容器中,并为 AsyncAnnotationBeanPostProcessor 的一些属性赋值。
核心类:AsyncAnnotationBeanPostProcessor
接下来,重点来了,在 AsyncAnnotationBeanPostProcessor 中做了很多事情,首先我们先看一下类关系图:
我们分析下这张图:
-
实现
BeanFactoryAware,在Spring创建bean前,先初始化BeanFactory,执行setBeanFactory()。 -
实现
BeanPostProcessor,在Spring创建bean前后,通过postProcessBeforeInitialization()和postProcessAfterInitialization(),执行一些逻辑。
我们再看下,AsyncAnnotationBeanPostProcessor 的核心代码:
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;
}
}
下面我们针对 AsyncAnnotationBeanPostProcessor 做的2个核心事情,进行详细分析:
- 在
setBeanFactory()方法中,创建AsyncAnnotationAdvisor对象。 - 在
AbstractAdvisingBeanPostProcessor类的AbstractAdvisingBeanPostProcessorAbstractAdvisingBeanPostProcessor()方法中,为@Async注解的方法设置代理。
增强器:AsyncAnnotationAdvisor
AsyncAnnotationAdvisor 为加 @Async 方法的处理器,我们还是来看下类关系图:
我们可以看到,AsyncAnnotationAdvisor 继承 PointcutAdvisor 、Advisor 接口,因此AsyncAnnotationAdvisor 的主要作用为加 @Async 方法的处理器,起到增强的作用(通知)。
下面我们分析下 AsyncAnnotationAdvisor :
① 构造方法:
public AsyncAnnotationAdvisor(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
// 支持Async与Asynchronous
Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
// 设置异步注解类型Async
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;
}
这个最终又是委托给 AnnotationAsyncExecutionInterceptor,它是一个具体的增强器,有着核心内容。
类关系图:
③ 核心增强器(过滤器)
在调用含有 @Async 的方法时,会通过拦截器,最终执行 invoke() 方法:
@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");
}
// 放到AsyncTaskExecutor执行Callable Task
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());
}
当获取异步结果时,支持 CompletableFuture ,ListenableFuture ,Future 的返回,并且支持 void 返回,doSubmit() 方法:
@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 { // 方法返回值为void
executor.submit(task);
return null;
}
}
④ 构建切点
根据 @Async 构建切点:
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);
}
设置代理过程
bean初始化完成后,为包含 @Async 的实例设置代理,过程如下:
@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;
}
}
// 判断bean中是否包含@Async注解,有则进入判断
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
// 为代理设置增强器,即AsyncAnnotationAdvisor
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader());
}
// No proxy needed.
return bean;
}
小结
注入核心类过程
- 通过
EnableAsync将AsyncConfigurationSelector注入到Spring容器中。 - 在
AsyncConfigurationSelector中注入ProxyAsyncConfiguration到Spring容器中。 - 在
ProxyAsyncConfiguration中通过@Bean将AsyncAnnotationBeanPostProcessor实例注入到Spring容器。
生成代理过程
- 在
AsyncAnnotationBeanPostProcessor实现BeanFactoryAware接口,在setBeanFactory()中构建增强器AsyncAnnotationAdvisor。- 在增强器构造方法中构建增强器
- 根据
@Async构造切点
- 初始化bean完成后,通过
postProcessAfterInitialization为包含@Async的类设置代理。
如何正确使用?
Impl类
@Service
public class AsyncDemoImpl implements AsyncDemo {
// 注入spring上下文
@Autowired
private ApplicationContext applicationContext;
@Override
public void a() {
System.out.println("开始执行a(): " + TimeUtil.getStringDate());
// 通过上下文获取该类的实例,再调用该实例方法
AsyncDemo asyncDemo = applicationContext.getBean(AsyncDemo.class);
asyncDemo.b();
System.out.println("执行结束a(): " + TimeUtil.getStringDate());
}
@Async
@Override
public void b() {
System.out.println("开始执行b(): " + TimeUtil.getStringDate());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行结束b(): " + TimeUtil.getStringDate());
}
}
执行结果
根据结果我们可以看到,确实变成了异步执行。
总结分析
那么为什么通过容器中获取的 asyncDemo 对象,调用 b() 方法,可以异步调用呢?
- 通过源码,我们知道
@Async标识的类,相当于aop方式,对b()方法进行了增强。 - 为
asyncDemo对象设置一个代理,通过代理调用已增强过的b()方法。 - 直接调用
b()方法,也就是this.b()(相当于new的实例),并未通过Spring容器中的asyncDemo实例调用 代理增强 的方法。