@Async 用了这么久,你真的了解吗 --- @Async核心原理解析

3,209 阅读6分钟

1598276505935

前言

相信很多朋友都使用过 @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();
    }

}

执行结果

1598002850197

通过执行结果,我们可以看到,@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;
		}
	}

}

由于 EnableAsyncAdviceMode 默认配置为 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 中做了很多事情,首先我们先看一下类关系图:

1598254191801

我们分析下这张图:

  • 实现 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 方法的处理器,我们还是来看下类关系图:

1598259606102

我们可以看到,AsyncAnnotationAdvisor 继承 PointcutAdvisorAdvisor 接口,因此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,它是一个具体的增强器,有着核心内容。

类关系图:

1598262024331

③ 核心增强器(过滤器)

在调用含有 @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());
}

当获取异步结果时,支持 CompletableFutureListenableFutureFuture 的返回,并且支持 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;
}

小结

注入核心类过程
  • 通过 EnableAsyncAsyncConfigurationSelector 注入到Spring容器中。
  • AsyncConfigurationSelector 中注入 ProxyAsyncConfiguration 到Spring容器中。
  • ProxyAsyncConfiguration 中通过 @BeanAsyncAnnotationBeanPostProcessor 实例注入到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());
    }
}

执行结果

1598270132900

根据结果我们可以看到,确实变成了异步执行。

总结分析

那么为什么通过容器中获取的 asyncDemo 对象,调用 b() 方法,可以异步调用呢?

  • 通过源码,我们知道 @Async 标识的类,相当于aop方式,对 b() 方法进行了增强。
  • asyncDemo 对象设置一个代理,通过代理调用已增强过的 b() 方法。
  • 直接调用 b() 方法,也就是 this.b() (相当于new的实例),并未通过Spring容器中的 asyncDemo 实例调用 代理增强 的方法。