Spring源码分析(二)如何解决循环依赖

在上一篇Spring源码分析中,咱们跳过了一部分关于Spring解决循环依赖部分的代码,为了填上这个坑,我这里另开一文来好好讨论下这个问题。java

首先解释下什么是循环依赖,其实很简单,就是有两个类它们互相都依赖了对方,以下所示:spring

@Component
public class AService {

    @Autowired
    private BService bService;
}
@Component
public class BService {
    
    @Autowired
    private AService aService;
}

AService和BService显然二者都在内部依赖了对方,单拎出来看仿佛看到了多线程中常见的死锁代码,但很显然Spring解决了这个问题,否则咱们也不可能正常的使用它了。缓存

所谓建立Bean实际上就是调用getBean() 方法,这个方法能够在AbstractBeanFactory这个类里面找到,这个方法一开始会调用getSingleton()方法。多线程

// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);

这个方法的实现长得颇有意思,有着一堆if语句。ide

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized(this.singletonObjects) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                               // 从三级缓存里取出放到二级缓存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }

    return singletonObject;
}

但这一坨if很好理解,就是一层层的去获取这个bean,首先从singletonObjects中获取,这里面存放的是已经彻底建立好的单例Bean;若是取不到,那么就往下走,去earlySingletonObjects里面取,这个是早期曝光的对象;若是仍是没有,那么再去第三级缓存singletonFactories里面获取,它是提早暴露的对象工厂,这里会从三级缓存里取出后放到二级缓存中。那么总的来讲,Spring去获取一个bean的时候,其实并非直接就从容器里面取,而是先从缓存里找,并且缓存一共有三级。那么从这个方法返回的并不必定是咱们须要的bean,后面会调用getObjectForBeanInstance()方法去获得实例化后的bean,这里就很少说了。源码分析

但若是缓存里面的确是取不到bean呢?那么说明这个bean的确还未建立,须要去建立一个bean,这样咱们就会去到前一篇生命周期中的建立bean的方法了。回顾下流程:实例化--属性注入--初始化--销毁。那么咱们回到文章开头的例子,有ServiceA和ServiceB两个类。通常来讲,Spring是按照天然顺序去建立bean,那么第一个要建立的是ServiceA。显然一开始缓存里是没有的,咱们会来到建立bean的方法。首先进行实例化阶段,咱们会来到第一个跟解决循环依赖有关的代码,在实例化阶段的代码中就能够找到。post

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

首先看看第一行,earlySingletonExposure这个变量它会是什么值?ui

它是有一个条件表达式返回的,一个个来看,首先,mbd.isSingleton()。咱们知道Spring默认的Bean的做用域都是单例的,所以这里正常来讲都是返回true没问题。第二个,this.allowCircularReference,这个变量是标记是否容许循环引用,默认也是true。第三个,调用了一个方法,isSingletonCurrentlyInCreation(beanName),进入该代码能够看出它是返回当前的bean是否是正常建立,显然也是true。所以这个earlySingletonExposure返回的就是true。this

接下来就进入了if语句的实现里面了,也就是addSingletonFactory()这个方法。看到里面的代码中出现singletonFactories这个变量是否是很熟悉?翻到上面的getSingleton()就知道了,其实就是三级缓存,因此这个方法的做用是经过三级缓存提早暴露一个工厂对象spa

/**
 * Add the given singleton factory for building the specified singleton
 * if necessary.
 * <p>To be called for eager registration of singletons, e.g. to be able to
 * resolve circular references.
 * @param beanName the name of the bean
 * @param singletonFactory the factory for the singleton object
 */
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

接下来,回忆下上一章节说的实例化以后的步骤,就是属性注入了。这就意味着ServiceA须要将ServiceB注入进去,那么显然又要调用getBean()方法去获取ServiceB。ServiceB尚未建立,则也会进入这个createBean()方法,一样也会来到这一步依赖注入。ServiceB中依赖了ServiceA,则会调用getBean()去获取ServiceA。此时的获取ServiceA可就不是再建立Bean了,而是从缓存中获取。这个缓存就是上面getSingleton()这个方法里面咱们看到的singletonFactory。那么这个singletonFactory哪里来的,就是这个addSingletonFactory()方法的第二个参数,即getEarlyBeanReference()方法。

/**
 * Obtain a reference for early access to the specified bean,
 * typically for the purpose of resolving a circular reference.
 * @param beanName the name of the bean (for error handling purposes)
 * @param mbd the merged bean definition for the bean
 * @param bean the raw bean instance
 * @return the object to expose as bean reference
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
         exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
      }
   }
   return exposedObject;
}

查看bp.getEarlyBeanReference(exposedObject, beanName)的实现,发现有两个,一个是spring-beans下的SmartInstantiationAwareBeanPostProcessor,一个是spring-aop下的AbstractAutoProxyCreator。咱们在未使用AOP的状况下,取的仍是第一种实现。

default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
   return bean;
}

那么使人惊讶的是,这方法直接返回了bean,也就是说若是不考虑AOP的话,这个方法啥都没干,就是把实例化建立的对象直接返回了。若是考虑AOP的话调用的是另外一个实现:

public Object getEarlyBeanReference(Object bean, String beanName) {
   Object cacheKey = getCacheKey(bean.getClass(), beanName);
   this.earlyProxyReferences.put(cacheKey, bean);
   return wrapIfNecessary(bean, beanName, cacheKey);
}

能够看出,若是使用了AOP的话,这个方法返回的其实是bean的代理,并非它自己。那么经过这部分咱们能够认为,在没有使用AOP的状况下,三级缓存是没有什么用的,所谓三级缓存实际上只是跟Spring的AOP有关的。

好了咱们如今是处于建立B的过程,但因为B依赖A,因此调用了获取A的方法,则A从三级缓存进入了二级缓存,获得了A的代理对象。固然咱们不须要担忧注入B的是A的代理对象会带来什么问题,由于生成代理类的内部都是持有一个目标类的引用,当调用代理对象的方法的时候,其实是会调用目标对象的方法的,因此因此代理对象是没影响的。固然这里也反应了咱们实际上从容器中要获取的对象其实是代理对象而不是其自己。

那么咱们再回到建立A的逻辑往下走,能看到后面实际上又调用了一次getSingleton()方法。传入的allowEarlyReference为false。

if (earlySingletonExposure) {
   Object earlySingletonReference = getSingleton(beanName, false);
   if (earlySingletonReference != null) {
      if (exposedObject == bean) {
         exposedObject = earlySingletonReference;
      }
      ...
   }
}

翻看上面的getSingleton()代码能够看出,allowEarlyReference为false就至关于禁用三级缓存,代码只会执行到经过二级缓存get。

singletonObject = this.earlySingletonObjects.get(beanName);

由于在前面咱们在建立往B中注入A的时候已经从三级缓存取出来放到二级缓存中了,因此这里A能够经过二级缓存去取。再往下就是生命周期后面的代码了,就再也不继续了。

那么如今就会有个疑问,咱们为何非要三级缓存,直接用二级缓存彷佛就足够了?

看看上面getEarlyBeanReference()这个方法所在的类,它是SpringAOP自动代理的关键类,它实现了SmartInstantiationAwareBeanPostProcessor,也就是说它也是个后置处理器BeanPostProcessor,它有着自定义的初始化后的方法。

/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

很明显它这里是earlyProxyReferences缓存中找不到当前的bean的话就会去建立代理。也就是说SpringAOP但愿在Bean初始化后进行建立代理。若是咱们只使用二级缓存,也就是在这个地方

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

直接调用getEarlyBeanReference()并将获得的早期引用放入二级缓存。这就意味着不管bean之间是否存在互相依赖,只要建立bean走到这一步都得去建立代理对象了。然而Spring并不想这么作,不信本身能够动手debug一下,若是ServiceA和ServiceB之间没有依赖关系的话,getEarlyBeanReference()这个方法压根就不会执行。总的来讲就是,若是不使用三级缓存直接使用二级缓存的话,会致使全部的Bean在实例化后就要完成AOP代理,这是没有必要的。

最后咱们从新梳理下流程,记得Spring建立Bean的时候是按照天然顺序的,因此A在前B在后:

循环依赖建立Bean的流程

咱们首先进行A的建立,但因为依赖了B,因此开始建立B,一样的,对B进行属性注入的时候会要用到A,那么就会经过getBean()去获取A,A在实例化阶段会提早将对象放入三级缓存中,若是没有使用AOP,那么本质上就是这个bean自己,不然是AOP代理后的代理对象。三级缓存singletonFactories会将其存放进去。那么经过getBean()方法获取A的时候,核心其实在于getSingleton()方法, 它会将其从三级缓存中取出,而后放到二级缓存中去。而最终B建立结束回到A初始化的时候,会再次调用一次getSingleton()方法,此时入参的allowEarlyReference为false,所以是去二级缓存中取,获得真正须要的bean或代理对象,最后A建立结束,流程结束。

因此Spring解决循环依赖的原理大体就讲完了,但根据上述的结论,咱们能够思考一个问题,什么状况的循环依赖是没法解决的?

根据上面的流程图,咱们知道,要解决循环依赖首先一个大前提是bean必须是单例的,基于这个前提咱们才值得继续讨论这个问题。而后根据上述总结,能够知道,每一个bean都是要进行实例化的,也就是要执行构造器。因此能不能解决循环依赖问题其实跟依赖注入的方式有关。

依赖注入的方式有setter注入,构造器注入和Field方式。

Filed方式就是咱们平时用的最多的,属性上加个@Autowired或者@Resource之类的注解,这个对解决循环依赖无影响;

若是A和B都是经过setter注入,显然对于执行构造器没有影响,因此不影响解决循环依赖;

若是A和B互相经过构造器注入,那么执行构造器的时候也就是实例化的时候,A在本身还没放入缓存的时候就去建立B了,那么B也是拿不到A的,所以会出错;

若是A中注入B的方式为setter,B中注入A为构造器,因为A先实例化,执行构造器,并建立缓存,都没有问题,继续属性注入,依赖了B而后走建立B的流程,获取A也能够从缓存里面能取到,流程一路通畅。

若是A中注入B的方式为构造器,B中注入A为setter,那么这个时候A先进入实例化方法,发现须要B,那么就会去建立B,而A还没放入三级缓存里,B再建立的时候去获取A就会获取失败。

好了,以上就是关于Spring解决循环依赖问题的全部内容,这个问题的答案我是好久以前就知道了,但真的只是知道答案,此次是本身看源码加debug一点点看才知道为啥是这个答案,虽然还作不到完全学的通透,但的确能对这个问题的理解的更为深入一点,再接再砺吧。

相关文章
相关标签/搜索