Spring @Async 使用时的注意事项

警告
本文最后更新于 2022-01-17,文中内容可能已过时。

@Async注解的方法所属的spring bean,不能注入到自身的属性中。

使用及问题

使用@Async注解需要在配置类中声明@EnableAsync,以启动此注解的支持。

当我们开启了@Async,并循环依赖当前bean时,启动会报错BeanCurrentlyInCreationException

解决方法,将这段异步逻辑单独抽取到另一个bean中。

案例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Slf4j
@Service
// TODO 即使加了懒加载,启动时不报错,但只要调用getBean()时,还是会报错。
//@Lazy
public class AsyncServiceImpl implements AsyncService {
    @Resource
    private AsyncService asyncService;

    @Async
    @Override
    public void asyncExecute() {
        asyncService.execute();
    }

    @Override
    public void execute() {
        log.info("异步执行业务代码...");
    }
}

原因

  1. 看注解@EnableAsync的声明
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// TODO 这里导入了一个ImportSelector
@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;
}
  1. @Import(AsyncConfigurationSelector.class)中,导入了一个ImportSelector
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
			    // TODO spring启动时,会加载这个类。这是一个 @Configuration 注解的类。
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}
  1. 看配置类中加载了什么
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@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");
        // TODO 异步注解bean处理器。所有@Async注解的方法,对应bean都会被它处理。
		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;
	}

}
  1. 继承关系如下

AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanPostProcessor

查看源码,异步代理对象是在bean初始化之后生成的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {

    /**
     * TODO 此方法是bean实例创建、依赖注入、初始化方法执行后,才执行的。
     */
    @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;
    }
}

spring中bean实例创建后会提前暴露引用,所以在依赖注入之前可以获取到bean实例。也是基于此实现了循环依赖注入。

@Async注解,是在依赖注入之后,才生成的代理对象,这是自相矛盾的。所以在使用@Async注解的方法所在类的bean不允许循环依赖注入。