摘要:Spring中总共有三种循环依赖,原型模式的循环依赖,单例模式的构造器循环依赖,单例模式的Setter注入循环依赖。
前面两种我已经写过了,在这里附上链接,有兴趣的可以去了解下,今天会将最后一种,也算是比较重要的一种。
原型模式的循环依赖:https://blog.csdn.net/lkp_kapila/article/details/105344252
单例模式的构造器循环依赖:https://blog.csdn.net/lkp_kapila/article/details/105347655
Spring中,默认属性是以setter方式注入。假如A依赖了B,B又依赖了A,那么在进行属性注入时会出现循环依赖的问题。而Spring确实通过了一种很巧妙的方式来解决了这种依赖的问题。下面先来看一组参数,Spring中的三级缓存。
/** 单例缓存池,这里存储的bean是已经完成了实例化,属性注入,初始化等一系列操作的bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 单例对象工厂缓存,存储了每个beanName所在的生产工厂,在调用构造生产实例时一般会加入到这里来 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 存储的提前暴露的早期对象,即只是调用了构造函数生成的bean对象 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
有了这三个缓存池,其实还不足以解决循环依赖,下面再说下另一个重要缓存。
/** 存储了当前正在创建的beanName */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
这个set集合用来存储当前正在创建的beanName,bean在创建之前,都会将其加入到这个集合中,直至所有的创建过程完成后,加入到singletonObjects单例缓存池中了,那么这个时候才会被移除。下面我们真正的进入源码,来剖析Spring是如何利用这几个缓存对象来解决循环依赖的,我们还是以A,B两个类来举例。
源码剖析
第一步:调用getBean(A)方法来获取A对象,活进入到具体的doGetBean方法,点进去看看到底做了什么?
//真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建 Object sharedInstance = getSingleton(beanName); //IOC容器创建单例模式Bean实例对象 if (sharedInstance != null && args == null) { //获取给定Bean的实例对象,主要是完成FactoryBean的相关处理 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { if (mbd.isSingleton()) { //这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象 sharedInstance = getSingleton(beanName, () -> { try { //创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义 return createBean(beanName, mbd, args); } catch (BeansException ex) { //显式地从容器单例模式Bean缓存中清除实例对象 destroySingleton(beanName); throw ex; } }); //获取给定Bean的实例对象 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } } return (T) bean; }
首先调用getSingleton(A)方法从缓存中进行获取,这个方法很重要,让我们点进去看看具体的实现。
//先从缓存中取是否已经有被创建过的单态类型的Bean //对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建 Object sharedInstance = getSingleton(beanName); protected Object getSingleton(String beanName, boolean allowEarlyReference) { //从单例bean池中获取,也就是一级缓存 Object singletonObject = this.singletonObjects.get(beanName); //当前池中没有,并且当前bean正在创建当中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //从早期暴露的对象工厂中获取到的object,此处表示调用构造方法生成后但未完成属性赋值的对象,也就是三级缓存 singletonObject = this.earlySingletonObjects.get(beanName); //早期bean为空,且运行提前引用 if (singletonObject == null && allowEarlyReference) { //获取提前暴露的对象工厂,对象在通过构造函数创建生成实例后与生成对象的工厂建立绑定关系,便于后续解决循环依赖,也就是三级缓存 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); //如果能提前获取到bean的对象工厂 if (singletonFactory != null) { //可以从工厂的getObject方法中找到绑定的具体的bean,此时的bean暂未完成属性赋值 singletonObject = singletonFactory.getObject(); //缓存升级,提升到二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
首先从singletonObjects单例缓存池中获取,因为此时的A才开始创建,那么池中肯定是没有的。接着进入if判断,执行isSingletonCurrentlyInCreation方法,看下具体实现。
public boolean isSingletonCurrentlyInCreation(String beanName) { //判断当前beanName是否正在创建当中 return this.singletonsCurrentlyInCreation.contains(beanName); }
这里不就是我们刚刚说的那个set集合吗?在这一步判断是否在集合中,我们都还没开始创建,也没见add操作,怎么可能在里面呢?所以返回false。此时的条件不满足,getSingleton(A)方法返回了null,然后回到doGetBean方法的主逻辑继续分析。此时,又调用了一个getSingleton(A,singletonFactory)方法,此getSingleton非彼getSingleton,让我们点进去看下具体的实现细节。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { //从单例缓存池中获取 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { ...... //将当前的beanName加入正在创建的集合中 beforeSingletonCreation(beanName); ...... try { //从对象工厂中获取具体的实例 singletonObject = singletonFactory.getObject(); newSingleton = true; }catch (IllegalStateException ex) { ...... }catch (BeanCreationException ex) { ...... } finally { //从正在创建的bean集合中移除 afterSingletonCreation(beanName); } } } //将beanName加入set集合中,表示当前bean正在创建 protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } //将beanName从set集合中移除,表示当前bean已经创建完毕,加入到了单例缓存池中 protected void afterSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } }
第一行从单例缓存池中取,肯定返回null,然后进入分支调用了beforeSingletonCreation方法,才正式的将A加入到正在创建的集合中。当程序执行到了singletonObject = singletonFactory.getObject()这一行时,此时会回调doGetBean方法中的createBean方法来创建A对象,然后进入到具体的创建doCreateBean方法,点进去看下具体实现逻辑。
//doGetBean方法中的createBean方法,创建bean protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { //创建Bean的入口 Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } //真正创建Bean的方法 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //...省略无关代码 //1.调用默认构造函数进行反射生成实例 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } //向容器中缓存单例模式的Bean对象,以防循环引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } //2.Bean对象的初始化,依赖注入在此触发 populateBean(beanName, mbd, instanceWrapper); //3.初始化Bean对象 exposedObject = initializeBean(beanName, exposedObject, mbd); //...省略无关代码 return exposedObject; }
doCreateBean方法中主要做了三件事情,调用createBeanInstance方法,底部通过反射获取到类的构造函数完成实例化。然后调用polulateBean方法进行属性的依赖注入,比如我们常见的@Autowired注入。最后调用initialzeBean方法完成bean的初始化工作。这些步骤我就不展开讲了,与本文的关系不是很大,有兴趣的可以看我的Spring源码之Bean的生命周期。但是在这个方法中,为了解决循环依赖,做了非常重要的一步操作。那就是在调用createBeanInstance方法刚创建好实例后,此时的bean并没有完成属性赋值,还是一个不完整的bean。此时先判断当前的bean是否为单例模式,并且是否允许循环依赖,并且是否正在创建当中,刚刚我们在前面已经将A加入到了set集合中,此时肯定满足条件。而在Spring中,所有的bean默认都是单例,且都允许循环依赖,如果想设置禁止循环依赖,可以在创建ApplicationContext上下文时对该属性值进行修改。让我们继续,此时的earlySingletonExposure变量返回true,紧接着会调用addSingletonFactory方法。这一步是解决循环依赖至关重要的一步,让我们点进去看看。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { //单例缓存池中是否包含当前bean,此时还没有创建完成,肯定不包含 if (!this.singletonObjects.containsKey(beanName)) { //将对象工程加入三级缓存 this.singletonFactories.put(beanName, singletonFactory); //清空掉二级缓存 this.earlySingletonObjects.remove(beanName); //将当前beanName加入到注册表中 this.registeredSingletons.add(beanName); } } } //首先会通过当前方法来获取到singletonFactory 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; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }
在调用addSingletonFactory方法之前,首先会调用getEarlyBeanReference方法来获取创建A的单例工厂对象singletonFactory。在Spring源码中解释的很清楚,让我们来看看。
/** * Obtain a reference for early access to the specified bean, * typically for the purpose of resolving a circular reference. * <p>This callback gives post-processors a chance to expose a wrapper * early - that is, before the target bean instance is fully initialized. * The exposed object should be equivalent to the what * {@link #postProcessBeforeInitialization} / {@link #postProcessAfterInitialization} * would expose otherwise. Note that the object returned by this method will * be used as bean reference unless the post-processor returns a different * wrapper from said post-process callbacks. In other words: Those post-process * callbacks may either eventually expose the same reference or alternatively * return the raw bean instance from those subsequent callbacks (if the wrapper * for the affected bean has been built for a call to this method already, * it will be exposes as final bean reference by default). */ default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; }
大致翻译一下,就是当前返回的对象工厂中呢包含了一个引用,而这个引用呢就是为了能够提前访问具体的bean,这么做的目的主要是为了解决循环依赖。继续回到addSingletonFactory方法,判断单例缓存池中是否存在A对象,由于A还没创建完成,所以返回null,流程继续。此时将singletonFacotry对象加入三级缓存中,便于以后循环依赖时可以提前引用。走完这一步之后,调用polulateBean方法进行属性注入。此时A发现依赖B,所以回过头去创建B,调用getBean(B)方法,前面的流程跟A的创建过程一模一样。此时三级缓存中的对象有A和B,当调用polulateBean方法进行属性注入,又发现依赖A,所以又会回过头去创建A,调用getBean(A)方法创建A,此时走到了doGetBean方法中的getSingleton(A)方法中,继续来分析一遍,看循环依赖是否已经被解决。
//先从缓存中取是否已经有被创建过的单态类型的Bean //对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建 Object sharedInstance = getSingleton(beanName); protected Object getSingleton(String beanName, boolean allowEarlyReference) { //从单例bean池中获取,也就是一级缓存 Object singletonObject = this.singletonObjects.get(beanName); //当前池中没有,并且当前bean正在创建当中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //从早期暴露的对象工厂中获取到的object,此处表示调用构造方法生成后但未完成属性赋值的对象,也就是三级缓存 singletonObject = this.earlySingletonObjects.get(beanName); //早期bean为空,且运行提前引用 if (singletonObject == null && allowEarlyReference) { //获取提前暴露的对象工厂,对象在通过构造函数创建生成实例后与生成对象的工厂建立绑定关系,便于后续解决循环依赖,也就是三级缓存 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); //如果能提前获取到bean的对象工厂 if (singletonFactory != null) { //可以从工厂的getObject方法中找到绑定的具体的bean,此时的bean暂未完成属性赋值 singletonObject = singletonFactory.getObject(); //缓存升级,提升到二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
此时的A仍然没在单例bean缓存池中,因为此时在循环调用创建A和B对象。同时也满足isSingletonCurrentlyInCreation(A)的条件。接着往下走,此时的earlySingletonObjects.get(A)也是没有的,流程继续,当走到this.singletonFactories.get(A)时,由于之前已经加入到三级缓存中,此时是可以获取到的,然后通过singletonFactory.getObject();方法获取到绑定好的A对象,此时的对象不过只是一个通过构造函数创建的实例而已。将三级缓存清掉,加入到二级缓存。
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { String currentlyCreatedBean = this.currentlyCreatedBean.get(); if (currentlyCreatedBean != null) { registerDependentBean(beanName, currentlyCreatedBean); } return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd); }
此时已经不为空了,可以直接返回给B,完成B对象中A的属性注入,此时的B完成创建后,又会回去继续完成A对象中B属性的注入,所以循环依赖得到了解决。