文章出處

通過《Spring讀書筆記——bean加載》和《Spring讀書筆記——bean解析》,我們明白了兩件事。

  • Spring如何加載消化一個xml配置文件
  • Spring如何將xml文件的各種標簽轉換為BeanDefinition并注冊到Spring容器下
    現在,我們理所當然的還差bean是如何被創建出來這一環節了。

從getBean說起

我們經常使用下面的方式實現先加載xml文件,然后獲取相應的bean實例

BeanFactory beanFactory = new ClassPathXmlApplicationContext("application-context.xml");
TestBean testBean = beanFactory.getBean("testBean");

顯然,我們是通過getBean方法獲取到的Bean實例,該方法是接口BeanFactory中定義的一個方法。具體實現在另外一個抽象類中,就是我們一會要說到的AbstractBeanFactory。

AbstractBeanFactory

該抽象類集成了FactoryBeanRegistrySupport并實現了ConfigurableBeanFactory接口(該接口間接實現了接口BeanFactory)

通過上面的區塊注釋以及提供的方法getBean,我們一眼就看出其余BeanFactory的密切關系。

getBean

該方法非常簡單,只是調用了一個函數,真正的實現都在doGetBean方法中了。代碼實現有點長,但是我們還是得靜下心來看看他到底做了哪些工作。

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {

   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check if bean definition exists in this factory.
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         String nameToLookup = originalBeanName(name);
         if (args != null) {
            // Delegation to parent with explicit args.
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
      }

      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // Guarantee initialization of beans that the current bean depends on.
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
         for (String dependsOnBean : dependsOn) {
            getBean(dependsOnBean);
            registerDependentBean(dependsOnBean, beanName);
         }
      }

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

      else if (mbd.isPrototype()) {
         // It's a prototype -> create a new instance.
         Object prototypeInstance = null;
         try {
            beforePrototypeCreation(beanName);
            prototypeInstance = createBean(beanName, mbd, args);
         }
         finally {
            afterPrototypeCreation(beanName);
         }
         bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      }

      else {
         String scopeName = mbd.getScope();
         final Scope scope = this.scopes.get(scopeName);
         if (scope == null) {
            throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
         }
         try {
            Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
               public Object getObject() throws BeansException {
                  beforePrototypeCreation(beanName);
                  try {
                     return createBean(beanName, mbd, args);
                  }
                  finally {
                     afterPrototypeCreation(beanName);
                  }
               }
            });
            bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
         }
         catch (IllegalStateException ex) {
            throw new BeanCreationException(beanName,
                  "Scope '" + scopeName + "' is not active for the current thread; " +
                  "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                  ex);
         }
      }
   }

歸一化beanName
如果看了前面兩篇文章,這里你就知道beanName就是注冊到Spring容器中的bean的名稱,具體來說就是放入BeanDefinitionMap中的一個鍵值對的key。
那么這里有什么好轉換處理的呢。
我們看下transformedBeanName的具體實現

public static String transformedBeanName(String name) {
   Assert.notNull(name, "'name' must not be null");
   String beanName = name;
   while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
      beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
   }
   return beanName;
}

沒錯,做了一個斷言處理,通過對于一種FactoryBean形式的bean做了處理。從FactoryBean這個命名就知道他是一種bean(不是病),*有關FactoryBean的介紹以及使用可以參看博文:http://blog.csdn.net/is_zhoufeng/article/details/38422549*,區別于BeanFactory(他是一個bean工廠,用于生產bean)。FactoryBean這種在注入bean的時候會在beanName前添加一個"&"修飾符,所以這里需要做歸一化處理。

嘗試從緩存中加載單例bean
Object sharedInstance = getSingleton(beanName);
該方法就是實現從緩存中獲取單例bean。
對于單例,應該都不陌生,單例bean,單例模式等等說的都是一個意思——一個東西只有一份。

  • Spring默認創建的bean就是單例bean,也就是在容器中只會存在一份這樣的bean
  • 這只是一次嘗試加載,如果加載不到,通過后面的代碼,我們可以發現其會從singletonFactories中加載
  • 加載單例bean可能會遇到一個頭疼的問題——循環依賴,就是加載A時,A依賴了B,那么需要加載B,發現B依賴了C,這時候去加載C,發現C依賴了A,這樣就形成了一個閉環,也就是循環依賴A->B->C-A。后面會說Spring是如何解決這個問題的
  • 有關Spring中不同類型的bean的循環依賴問題和解決方法可以參看*https://my.oschina.net/yibuliushen/blog/737640*

bean實例化
假設我們從緩存中得到了bean,但是這還不是bean的最終狀態,可以認為這只是一個引用,要獲得真正的bean實例,我們還需要看下getObjectForBeanInstance方法。

原型模式的循環依賴檢查
這里引用下上面提供有關循環依賴的鏈接中比較重要的內容

spring循環依賴的情況
1.構造器注入屬性依賴(A B兩個對象都使用構造方法,注入依賴的屬性)
無論是單例,還是原型對象,只要是通過構造器注入的屬性依賴,都會報錯,循環依賴錯誤 org.springframework.beans.factory.BeanCurrentlyInCreationException:
原因:試想,構造器是創建對象的入口方法,構造的時候都循環依賴了,我這個對象壓根就創建不了啊。那肯定是無法解決的,大羅神仙也無能為力。
2.setter方法注入屬性依賴
這個spring完美解決了,支持這種循環依賴
原理:創建對象A的時候,先通過無參構造方法創建一個實例,此時屬性都是空的,但是對象引用已經創建出來,然后把A的引用提前暴露出來。然后setter B屬性的時候,創建B對象,此時同樣通過無參構造方法構造然后將對象引用暴露出來。接著B執行setter方法,去池中找A,能找到A(因為此時A已經暴露出來,有指向改對象的引用了),這么依賴B就構造完成,也初始化完成,然后A接著初始化完成。---循環依賴就這么解決了
3.原型對象的屬性依賴(當然指的是通過setter方法注入依賴)
這個spring也無能為力,因為是原型對象,A創建的時候不會提前暴露出來,所以,每次都是要創建,創建的時候,發現有相同的對象正在創建,同樣報錯,循環依賴錯誤,同第一種情況類似。

我們知道對于單例默認的循環依賴,我們是可以解決的,但是對于原型類型的循環依賴,我們沒有辦法解決,所以這里通過對于原型bean的檢查適時拋出異常。

if (isPrototypeCurrentlyInCreation(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
}

檢測parentBeanFactory
順著代碼的邏輯,很自然的來到了從parentBeanFactory中加載Bean的模塊。當在緩存中沒有加載到Bean的時候,我們就會從parentBeanFactory中試試。

轉換為RootBeanDefinition
前兩篇,我們介紹了從xml標簽到BeanDefinition的轉變,這里我們需要將GenericBeanDefinition轉為RootBeanDefinition,這是處于后面的處理是一致對于RootBeanDefinition處理考量的。

解決bean依賴的問題

// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
   for (String dependsOnBean : dependsOn) {
      getBean(dependsOnBean);
      registerDependentBean(dependsOnBean, beanName);
   }
}

從注釋就可以知道,這塊主要是解決在初始化一個bean的時候,這個bean依賴了其他的bean,所以需要在創建之前先初始化依賴的bean。

對于scope的處理以及對于類型轉換的處理
后面剩下的代碼主要是對于Spring中不同scope的處理,比如singleton、prototype、request、session等等。

上面就是getBean這一抽象層次上關于如何創建bean的詳細過程,下面對于其中一些部分做詳細解釋。

等等,寫著寫著發現后面的點還是有點多,一篇恐怕撐不住了,還是分上下集吧~~~

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優質文章。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()