文章出處

有關Spring加載bean系列,今天這是最后一篇了,主要接上篇對于從Spring容器中獲取Bean的一些細節實現的補充。

  • 《Spring讀書筆記——bean加載》——Spring如何加載消化一個xml配置文件

  • 《Spring讀書筆記——bean解析》——Spring如何將xml文件的各種標簽轉換為BeanDefinition并注冊到Spring容器下

  • 《Spring讀書筆記——bean創建(上)》——概述Spring如何從容器中取出需要的那個Bean

從緩存中加載單例

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

在看這段代碼之前,我們先了解下Spring對于單例bean出現循環依賴的解決方法。如果出現上面我們介紹的A->B->C->A的情況,那是不是說Spring就無能為力了,顯然Spring沒有那么弱。那么Spring是怎么做的?
鑒于單例bean的循環依賴問題,Spring創建bean的原則是不等bean創建完成就會將bean的ObjectFactory提前曝光加入到緩存中,一旦有某個bean創建時需要依賴這個bean了,那么就可以直接使用ObjectFactory。
簡單說,創建bean的時候,就是打包快遞發貨,主管為了知道你今天要派發多少個包裹,為了節省大家時間以及以免統計漏掉的情況。你可以先拿出一個包裹箱子,上面寫上要寄收件人、收貨地址、聯系方式等等,但是這時候還沒有往里面打包真正的快遞。
這里曝光的bean就相當于這個快遞箱子。

好了,知道了這個原則之后,我們就好理解代碼了。
首先從singletonObjects中獲取實例,取不到則從earlySingletonObjects中獲取,仍然取不到,我們還可以到singletonFactories中獲取相應的ObjectFactory,在調用這個ObjectFactory的getObject方法來創建bean。
然后將其加入到earlySingletonObjects中,在將其從singletonFactories中刪除。

想必,你已經被這些用來存儲和刪除的集合搞瘋了,沒關系,我們來理一下:

  • singletonObjects

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

    用于保存BeanName和創建bean實例之間的關系。

  • singletonFactories:

    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);

    用于保存BeanName和創建bean的工廠之間的關系

  • earlySingletonObjects:

    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    用于保存BeanName和創建bean的工廠之間的關系,與singletonObjects的區別是當一個bean被放入這個集合后,可以用于其他bean做循環依賴檢查

bean實例化

我們從緩存中拿到bean之后,就需要根據bean的不同類型做不同的處理,返回相應的bean,實現這個功能的就是getObjectForBeanInstance方法

protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

        // Don't let calling code try to dereference the factory if the bean isn't a factory.
        if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }

        // Now we have the bean instance, which may be a normal bean or a FactoryBean.
        // If it's a FactoryBean, we use it to create a bean instance, unless the
        // caller actually wants a reference to the factory.
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

        Object object = null;
        if (mbd == null) {
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }
  • 首先檢測指定的beanName是否是工廠bean相關,如果既不是工廠bean又是以"&"開頭,則校驗失敗,拋出異常
  • 如果這個bean不是工廠Bean(FactoryBean),那么就直接返回bean實例
  • 剩下代碼就是處理FactoryBean,我們順著這樣的順序依次來到getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean

    private Object doGetObjectFromFactoryBean(
            final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
            throws BeanCreationException {
    
        Object object;
        try {
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        public Object run() throws Exception {
                                return factory.getObject();
                            }
                        }, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                object = factory.getObject();
            }
        }
        catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }
    
    
        // Do not accept a null value for a FactoryBean that's not fully
        // initialized yet: Many FactoryBeans just return null then.
        if (object == null && isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(
                    beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
    
        if (object != null && shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
            }
        }
    
        return object;
    }

    這么長的代碼,如果嫌累,就只看factory.getObject()這行就好,這詮釋了FactoryBean的加載時通過factory.getObject的方式獲取到對應的bean實例的。

如何創建單例bean

在上篇的doGetBean方法中,如果從緩存中加載不到,那么我們就需要老老實實的從頭開始加載bean了,對于單例bean的加載就都在這里實現了

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

創建bean

我們從AbstractBeanFactory的createBean方法來到了AbstractAutowiredCapableBeanFactory的createbean方法,而真正的創建bean其實在doCreateBean方法中

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
   Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         mbd.postProcessed = true;
      }
   }

   // 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.isDebugEnabled()) {
         logger.debug("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      addSingletonFactory(beanName, new ObjectFactory() {
         public Object getObject() throws BeansException {
            return getEarlyBeanReference(beanName, mbd, bean);
         }
      });
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      populateBean(beanName, mbd, instanceWrapper);
      if (exposedObject != null) {
         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);
      }
   }

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

   // Register bean as disposable.
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}
  • 如果是單例Bean,那么首先是從factoryBeanInstanceCache中清除該beanName對應的記錄
  • 實例化bean,將BeanDefinition轉換為BeanWrapper對象
  • bean合并后的處理
  • 解決循環依賴問題
  • 屬性填充,將所有屬性填充到bean的實例中
    這個方法,本身不算長,但是層層深入就會發現其下面包羅了創建bean的諸多繁雜的細節(這塊自己看的也是云里霧里,就不往下延伸擴展了)。

雖然對于Spring加載bean,我只寫了四篇,但是其內部實現遠比我表述的要復雜的多。
看源碼確實很煎熬,對于目前看不懂的地方要么多看幾遍,要么先跳過。閱讀代碼的過程中要懂得取舍,對于非重點部分比如日志或者異常處理可以先忽略,沿著一條主線往下看,最主要是先弄懂代碼的只要意圖。
Spring的bean加載代碼量雖然巨大,但是思路還是比較清晰的,我們知道Spring如何加載xml然后解析xml,再到如何把xml的元素轉為自己的BeanDefinition,最后又是如何取出對應的beanName然后返回一個bean實例供容器使用的。

網上有一位大神用一張圖就把整個過程畫出來了

注:圖片來源http://blog.csdn.net/zghwaicsdn/article/details/50910384

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


文章列表


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

    IT工程師數位筆記本

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