Spring 重点难点源码分析: 循环引用

循环引用只支持在单例情况,此处详细讨论单例

参看 AbstractBeanFactory.doGetBean , 在单例创建时不是直接新建实例的,而是传入了回调方法。在创建前后,分别在创建前后加入 singletonsCurrentlyInCreation 状态。

// Create bean instance.
if (mbd.isSingleton()) {
   sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
      public Object getObject() throws BeansException {
         try {
            return createBean(beanName, mbd, args);
         }
         catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
         }
      }
   });
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

具体getSingleton方法如下

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "'beanName' must not be null");
   synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while the singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
         }
         beforeSingletonCreation(beanName);
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<Exception>();
         }
         try {
            singletonObject = singletonFactory.getObject();
         }
         catch (BeanCreationException ex) {
            if (recordSuppressedExceptions) {
               for (Exception suppressedException : this.suppressedExceptions) {
                  ex.addRelatedCause(suppressedException);
               }
            }
            throw ex;
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            afterSingletonCreation(beanName);
         }
         addSingleton(beanName, singletonObject);
      }
      return (singletonObject != NULL_OBJECT ? singletonObject : null);
   }

在如上方法中注意到父类 DefaultSingletonBeanRegistry 几个变量

singletonsCurrentlyInCreation
单例在调用回调方法的前与后,分别通过beforeSingletonCreation及afterSingletonCreation加入和去除beanName

singletonObjects, registeredSingletons
单例完全创建成功后,addSingleton添加这两个参数

singletonFactories
doGetBean 中的回调方法,会调用 createBean,然后调用 AbstractAutowireCapableBeanFactory 中的doCreateBean,
在调用populateBean(beanName, mbd, instanceWrapper);进行属性注入前,有这一段
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, new ObjectFactory<Object>() {
      public Object getObject() throws BeansException {
         return getEarlyBeanReference(beanName, mbd, bean);
      }
   });
}
这是为了在属性注入前提前暴露未完成的bean实例,当其它实例有对它的引用,即可以在该实例还未属性注入时,就可以作引用赋值 

如果是单例且是创建中的状态,则认为需要earlySingletonExposure(早期静态暴露),由于此方法是在beforeSingletonCreation之后afterSingletonCreation之前调用的,则对单例肯定是要做早期静态暴露的。此处调用

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);
      }
   }
}

在此处singletonFactories赋值,其值为一回调方法,该方法中调用 getEarlyBeanReference,在调用该方法时对方法应用BeanPostProcessors,回调方法的用处就是,不必现在就应用BeanPostProcessors,但是现在就可以把对象暴露出去。

earlySingletonObjects

那把对象暴露出去有什么用呢?在下面这个例子下 TestA,TestB分别使用属性注入的方式握有对方法的引用

<bean id="testA" class="com.test.TestA">
    <property name="testB" ref="testB"></property>
</bean>
<bean id="testB" class="com.test.TestB">
    <property name="testA" ref="testA"></property>
</bean>

在AbstractAutowireCapableBeanFactory中看到三个方法的调用顺序分别是先调用

1 createBeanInstance  ---创建实例
2 addSingletonFactory ---暴露实例引用工厂
3 populateBean  --注入属性依赖

在getBean("TestA")时,会在执行populateBean时发现TestB依赖,此时必须要调用getBean("TestB"), 而在调用getBean("TestB")时又会在执行到populateBean时发现需要调用getBean("TestA")

在getBean("TestB")时执行到populateBean时调用getBean("TestA")时,此时情况变了,参考AbstractBeanFactory.doGetBean的getSingleton方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

第一次调用getBean("TestA")时,isSingletonCurrentlyInCreation判断为false,不过第二次调用时,由于TestA已经执行了创建实例及暴露引用两步,此时isSingletonCurrentlyInCreation判断为true,此时就会把singletonFactory中的引用取出,执行getObject()方法,把执行了BeanPostProcessors后的实例,放入earlySingletonObjects。然后把实例返回供getBeat("TestB")注入。TestB完成注入后再把实例供TestA注入

在注入完成后,还会经历下面这段验证,见AbstractAutowireCapableBeanFactory.doCreateBean下面,由于TestA会执行两次getBean("TestA")且在第二次时往 earlySingletonObjects 放了对象,所以此处earlySingletonReference不为空

具体调用的链如下

 getBean(name=testA)
 doGetBean(name=testA, requiredType=null, args=null, typeCheckOnly=false)
 getSingleton(beanName=testA, singletonFactory=org.springframework.beans.factory.support.AbstractBeanFactory$1@96def03)
 getObject()
 createBean(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
 doCreateBean(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
 populateBean(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestA@59d016c9])
 applyPropertyValues(beanName=testA, mbd=Root bean: class [com.codeanalysis.TestA]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestA@59d016c9], pvs=PropertyValues: length=1; bean property testB)
 resolveValueIfNecessary(argName=bean property testB, value=<testB>)
 resolveReference(argName=bean property testB, ref=<testB>)

 getBean(name=testB)
 doGetBean(name=testB, requiredType=null, args=null, typeCheckOnly=false)
 getSingleton(beanName=testB, singletonFactory=org.springframework.beans.factory.support.AbstractBeanFactory$1@cd3fee8)
 getObject()
 createBean(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
 doCreateBean(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], args=null)
 populateBean(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestB@63021689])
 applyPropertyValues(beanName=testB, mbd=Root bean: class [com.codeanalysis.TestB]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring/circularReferenceTest.xml], bw=org.springframework.beans.BeanWrapperImpl: wrapping object [com.codeanalysis.TestB@63021689], pvs=PropertyValues: length=1; bean property testA)
 resolveValueIfNecessary(argName=bean property testA, value=<testA>)
 resolveReference(argName=bean property testA, ref=<testA>)

 getBean(name=testA)
 doGetBean(name=testA, requiredType=null, args=null, typeCheckOnly=false)
 getSingleton(beanName=testA)

此时返回 TestA@59d016c9. 实例

 

由于doCreateBean的过程是先暴露引用,再属性注入,再调用init-method初始化。init-method初始化过程可能会导致exposedObject变化。

如果 exposedObject != bean,则要看有没有依赖了,因为依赖都是依赖的bean引用,如果有,那就报错。

if (earlySingletonExposure) {
   Object earlySingletonReference = getSingleton(beanName, false);
   if (earlySingletonReference != null) {
      if (exposedObject == bean) {
         exposedObject = earlySingletonReference;
      }
      else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
         String[] dependentBeans = getDependentBeans(beanName);
         Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
         for (String dependentBean : dependentBeans) {
            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 " +
                  "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
         }
      }
   }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值