前言
之前在项目中使用@Async,会出现循环依赖问题,我蛮感兴趣的,所以翻了翻源码看看原因
EnableAsync
在翻看原因之前,需要有一些前置知识的铺垫,我们先看看@Async这个注解异步的实现方式
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
// 配置异步注解,默认是@Async
Class<? extends Annotation> annotation() default Annotation.class;
// 默认使用jdk动态代理
boolean proxyTargetClass() default false;
// 默认使用Spring AOP代理
AdviceMode mode() default AdviceMode.PROXY;
// 这个注解会往容器中添加一个类AsyncAnnotationBeanPostProcessor,这个配置代表这个类的执行顺序
int order() default 2147483647;
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
public AsyncConfigurationSelector() {
}
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch(adviceMode) {
// 默认使用Spring AOP代理
case PROXY:
return new String[]{ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
default:
return null;
}
}
}
在之前的Spring文章中,我写过多次,@Import这个注解可以引入三种类型的类
- ImportSelector类型
- ImportBeanDefinitionRegistrar类型
- 普通类
不管哪种类型,都是注册BeanDefinition的一种方式,此处Import的是ImportSelector,进入AsyncConfigurationSelector,会发现根据代理类型注册了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 bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
// 对AsyncAnnotationBeanPostProcessor这个类的属性进行赋值
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;
}
}
asyncAdvisor()方法中主要是对AsyncAnnotationBeanPostProcessor这个类进行属性赋值,那这些值是怎么获取的,去看看父类AbstractAsyncConfiguration,代码很简单易懂,是从注解中获取的值,由于父类先初始化,所以子类可以拿到注解的值
public abstract class AbstractAsyncConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
}
回过头来看AsyncAnnotationBeanPostProcessor这个类,看这个类的名字,可以猜测到这个类实现了BeanPostProcessor,我们进一步看看这个类的继承关系
不出所料,这个类实现了BeanPostProcessor,还实现了BeanClassLoaderAware、BeanFactoryAware,如果对Bean的生命周期比较熟悉的同学,应该知道这几个类的作用,以及他们的执行顺序,不太熟悉的同学可以看看我之前写的这篇文章juejin.cn/post/698431…,下图是Bean的生命周期
AsyncAnnotationBeanPostProcessor
既然实现了BeanPostProcessor、BeanClassLoaderAware、BeanFactoryAware,相信我们只要搞懂这个类在bean生命周期各个过程中做了什么便可以了解异步的实现方式
setBeanFactory
这个阶段非常重要,定义了通知(Advice)与切点(Pointcut)
@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;
}
public AsyncAnnotationAdvisor(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
// 添加异步注解,默认为@Async和EJB 3.1规范下的@Asynchronous
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) {
// 初始化了一个拦截器,如果对AOP比较熟悉的同学,就知道里面有个invoke方法
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
Pointcut作为Spring AOP最顶级的抽象,主要负责对系统相应Joinpoint的捕获,如果把Joinpoint比做数据,那么Pointcut就是查询条件,一个Pointcut可以对应多个Joinpoint。ClassFilter和MethodMatcher分别限定在不同级别上对于Joinpoint的匹配,ClassFilter是类级别,MethodMatcher是方法级别,AnnotationMatchingPointcut实现了Pointcut
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);
// 存在多个注解的情况下,会合并起来,union方法内部做了合并
if (result == null) {
result = new ComposablePointcut(cpc);
}
else {
result.union(cpc);
}
result = result.union(mpc);
}
return (result != null ? result : Pointcut.TRUE);
}
postProcessBeforeInitialization
这个阶段返回bean,会继续往下走bean的生命周期,没有做什么
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// advisor为null或者该类为AOP的基础设施类,不进行代理
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
// 这个地方比较关键
// 有些同学会比较困惑,当一个方法加了@Async、@Transactional注解,是会先执行事务的切面还是异步的切面
// 答案就在这里
// 已经被代理的类,不会重复生成代理,而是会将增强器添加到代理中
// 由于AbstractAdvisingBeanPostProcessor的构造器中已经把beforeExistingAdvisors设置为true
// 所以异步的增强器放在了第一位
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;
}
通知
直接看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");
}
// 定义一个Callable异步线程
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());
}
这里还有一个关键点,异步使用的线程池,如果我们不在@Async注解上配置的话,会使用默认的线程池
@Nullable
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor;
// 获取@Async上配置的线程池
String qualifier = getExecutorQualifier(method);
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
else {
// 获取默认的线程池
targetExecutor = this.defaultExecutor.get();
}
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
}
循环依赖
想要知道@Async为什么会导致循环依赖,必须先看看正常情况下循环依赖是怎么解决的,解决循环依赖的核心是三级缓存
juejin.cn/post/698655… 我的这篇文章有比较详细的讲解, 这里就不赘述了,下面讲讲产生循环依赖的原因
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
......
// mbd.isSingleton():bean是否是单例
// this.allowCircularReferences:是否允许出现循环依赖
// isSingletonCurrentlyInCreation(beanName):bean是否在创建中
// 如果三个条件都满足,说明出现了循环依赖
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");
}
// 将bean放到三级缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性赋值
populateBean(beanName, mbd, instanceWrapper);
// bean初始化
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);
}
}
......
return exposedObject;
这段代码是获取bean的一段关键代码,假设有A、B两个类互相循环引用
创建A的过程是这样的
A->B(创建A,必须先创建B),此时A会提前放进三级缓存中
B->A(创建B,又必须先创建A,因为A的引用已经提前暴露了),B可以去三级缓存中获取A
具体缓存中获取A的代码如下
// 这段代码三级缓存容器都涉及到了,初看有点懵,但是思路不难,先从一级缓存找,找不到去二级缓存找,还找不到去三级缓存
// 如果在三级缓存找到了,就放到二级缓存中,非常像数据库跟redis的缓存策略,先到redis中找,找不到去数据库找,找到再写到redis中
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先从一级缓存中找bean
Object singletonObject = this.singletonObjects.get(beanName);
// 这里有两个判断,singletonObject == null 表示在一级缓存中找不到bean
// isSingletonCurrentlyInCreation()表示该bean是否在创建中
// 如果两个条件满足,说明出现了循环依赖
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 再从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 这里又有两个判断
// singletonObject == null 表示在二级缓存中没有获取到
// allowEarlyReference 这个值传进来就为true,表示是否允许获取早期引用
// 如果两个条件都满足,就到三级缓存中获取
if (singletonObject == null && allowEarlyReference) {
// 从三级缓存获取,注意,这里获取到是一个ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 通过ObjectFactory获取bean实例,这里是循环依赖的关键
singletonObject = singletonFactory.getObject();
// 打怪升级,从三级缓存升级到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
// 返回bean
return singletonObject;
}
singletonObject = singletonFactory.getObject();
注意这一行代码,放在三级缓存中的是一个工厂类,在缓存中获取A的时候会顺便执行一段定制代码,代码如下,这段代码会生成一个代理对象
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;
// 这里会生成一个代理对象
// 我们回忆一下,上文讲@Async的原理的时候是不是有一个地方会生成一个代理对象
// 这里又会生成一个代理对象,循环依赖的时候到底注入的是哪一个?
// 注入的是在这里生成的代理对象
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
看到这里,有些同学估计会很奇怪,生成两个代码对象会有什么影响?
我们都知道默认情况下,Spring中的bean是单例的,现在生成了两个代理对象,像Spring这么严谨的框架,肯定就是报错了
if (earlySingletonExposure) {
// 还是上面A、B互相依赖的例子,假设B初始化完成,已经注入了A的代理对象
// A继续初始化(initializeBean),经过AsyncAnnotationBeanPostProcessor的处理,会生成一个新的代理对象
// getSingleton会从二级缓存中拿到B初始化时生成的A的代理对象
Object earlySingletonReference = getSingleton(beanName, false);
// 正常情况下bean只会放在三级缓存,earlySingletonReference不为null表示存在循环依赖
if (earlySingletonReference != null) {
// AsyncAnnotationBeanPostProcessor生成了代理,exposedObject跟bean已经不相等了
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping默认为false
// hasDependentBean表示其它bean依赖了这个bean
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 获取依赖这个bean的所有bean
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 如果依赖这个bean的bean有创建好的
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.");
}
}
}
}