Spring源码解析-@Async循环依赖

429 阅读8分钟

前言

之前在项目中使用@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,我们进一步看看这个类的继承关系

image.png 不出所料,这个类实现了BeanPostProcessor,还实现了BeanClassLoaderAware、BeanFactoryAware,如果对Bean的生命周期比较熟悉的同学,应该知道这几个类的作用,以及他们的执行顺序,不太熟悉的同学可以看看我之前写的这篇文章juejin.cn/post/698431…,下图是Bean的生命周期

image.png

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.");
            }
         }
      }
   }
​
​

参考资料

blog.csdn.net/qq_18297675…

blog.csdn.net/weixin_3786…