1. 程式人生 > >Spring原始碼分析之-載入IOC容器

Spring原始碼分析之-載入IOC容器

本文接上一篇文章 SpringIOC 原始碼,控制反轉前的處理(https://mp.weixin.qq.com/s/9RbVP2ZQVx9-vKngqndW1w) 繼續進行下面的分析

首先貼出 Spring bean容器的重新整理的核心 11個步驟進行祭拜(一定要讓我學會了...阿門)

// 完成IoC容器的建立及初始化工作
@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {

    // 1: 重新整理前的準備工作。
    prepareRefresh();

    // 告訴子類重新整理內部bean 工廠。
    //  2:建立IoC容器(DefaultListableBeanFactory),載入解析XML檔案(最終儲存到Document物件中)
    // 讀取Document物件,並完成BeanDefinition的載入和註冊工作
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    //  3: 對IoC容器進行一些預處理(設定一些公共屬性)
    prepareBeanFactory(beanFactory);

    try {

      //  4:  允許在上下文子類中對bean工廠進行後處理。
      postProcessBeanFactory(beanFactory);

      //  5: 呼叫BeanFactoryPostProcessor後置處理器對BeanDefinition處理
      invokeBeanFactoryPostProcessors(beanFactory);

      //  6: 註冊BeanPostProcessor後置處理器
      registerBeanPostProcessors(beanFactory);

      //  7: 初始化一些訊息源(比如處理國際化的i18n等訊息源)
      initMessageSource();

      //  8: 初始化應用事件多播器
      initApplicationEventMulticaster();

      //  9: 初始化一些特殊的bean
      onRefresh();

      //  10: 註冊一些監聽器
      registerListeners();

      //  11: 例項化剩餘的單例bean(非懶載入方式)
      //      注意事項:Bean的IoC、DI和AOP都是發生在此步驟
      finishBeanFactoryInitialization(beanFactory);

      //  12: 完成重新整理時,需要釋出對應的事件
      finishRefresh();
    }

    catch (BeansException ex) {
      if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
      }

      // 銷燬已經建立的單例避免佔用資源
      destroyBeans();

      // 重置'active' 標籤。
      cancelRefresh(ex);

      // 傳播異常給呼叫者
      throw ex;
    }

    finally {

      // 重置Spring核心中的常見內省快取,因為我們可能不再需要單例bean的元資料了...
      resetCommonCaches();
    }
  }
}

下面來分析上述流程中的第二個步驟: 建立並解析IOC容器

我們開始進入分析,首先會呼叫到 AbstractApplicationContext 中的 obtainFreshBeanFactory 方法,此方法會進行IOC容器的重新整理並取出BeanFactory,程式碼如下

// 告訴內部子類重新整理內部的bean factory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 主要是通過該方法完成IoC容器的重新整理
        // ClassPathXmlApplicationContext.refreshBeanFactory 呼叫的是 AbstractRefreshApplicationContext
        // AnnotationConfigApplicationContext.refreshBeanFactory呼叫的是GenericApplicationContext
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    ...
        return beanFactory;
    }
  1. 首先第一步, refreshBeanFactory,重新整理Bean工廠,refreshBeanFactory是一個介面,此介面在 AbstractApplicationContext 中進行定義,先看一下這個JavaDoc對這個介面的定義
/**
     * 子類必須實現這個方法才能執行實際的配置載入。此方法在任何其他初始化工作開始之前由refresh()方法呼叫
     * 一個子類建立了一個新的 bean factory 並且持有對它的引用,或者返回它擁有的單個BeanFactory例項。
     * 在後一種情況下,如果多次重新整理上下文,它通常會丟擲 IllegalStateException。
     */
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

它有兩個實現類,預設的是 AbstractRefreshableApplicationContext 類,它的refreshBeanFactory方法如下

@Override
protected final void refreshBeanFactory() throws BeansException {
  // 如果之前有IoC容器,則銷燬
  if (hasBeanFactory()) {
    destroyBeans();
    closeBeanFactory();
  }
  try {
    // 建立IoC容器,也就是 DefaultListableBeanFactory, 初始化AbstractBeanFactory
    // 註冊BeanNameAware,BeanClassLoaderAware,BeanFactoryAware, 設定當前BeanFactory
    DefaultListableBeanFactory beanFactory = createBeanFactory();
    beanFactory.setSerializationId(getId());
    // 設定工廠的屬性:是否允許BeanDefinition覆蓋和是否允許迴圈依賴
    customizeBeanFactory(beanFactory);
    // 呼叫載入BeanDefinition的方法,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器
    loadBeanDefinitions(beanFactory);
    synchronized (this.beanFactoryMonitor) {
      this.beanFactory = beanFactory;
    }
  }
  catch (IOException ex) {
    throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  }
}

如果之前有IOC容器的話,就進行銷燬,並且關閉容器。適用於第二次呼叫ClassPathXmlApplicationContext("配置檔案"),由於是第一次載入配置檔案,還未建立BeanFactory,所以hasBeanFactory()返回false。

下面這一步就是建立IOC容器,也就是DefaultListableBeanFactory

// 為上下文建立一個內部工廠,預設的實現建立了一個內部的DefaultListableBeanFactory.getInternalParentBeanFactory(),
// 在子類中被重寫,例如自定義DefaultListableBeanFactory的設定。
protected DefaultListableBeanFactory createBeanFactory() {
  return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

上述方法傳遞的是一個getInternalParentBeanFactory()方法,我們先來看一下這個方法:

protected BeanFactory getInternalParentBeanFactory() {
  return (getParent() instanceof ConfigurableApplicationContext) ?
    ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}

getParent()這個方法會返回父類上下文,如果沒有(父類)的話,返回null(也就表明這個上下文是繼承樹的根節點。)這句程式碼的意思也就是說如果它實現了 ConfigurableApplicationContext,則返回父類上下文的BeanFactory,如果沒有的話,就返回父上下文字身。因為沒有設定過父節點上下文,所以此時的getInternalParentBeanFactory()方法返回為null。

下一步返回一個DefaultListableBeanFactory,首先來看一下DefaultListableBeanFactory的繼承體系

它的呼叫流程如下:

  • AbstractRefreshableApplicationContext類中返回一個DefaultListableBeanFactory的建構函式
  • DefaultListableBeanFactory建構函式中呼叫AbstractAutowireCapableBeanFactory的建構函式
  • AbstractAutowireCapableBeanFactory帶引數的建構函式會呼叫AbstractAutowireCapableBeanFactory無引數的建構函式,並註冊了BeanNameAware、BeanFactoryAware、BeanClassLoaderAware介面。並設定setParentBeanFactory(parentBeanFactory) 為null(parentBeanFactory就是上面getInternalParentBeanFactory()方法的返回值);

  • AbstractAutowireCapableBeanFactory會呼叫父類AbstractBeanFactory無參構造方法並初始化。

此時的DefaultListableBeanFactory會帶上一系列的初始化欄位。Bean工廠說的也就是DefaultListableBeanFactory

  1. 在createBeanFactory()之後,會給ClassPathXmlApplicationContext設定全域性唯一Id
beanFactory.setSerializationId(getId());
  1. 第三步,設定DefaultListableBeanFactory的屬性,是否允許重寫Bean的定義資訊和是否允許迴圈依賴。沒有設定,預設為null
/*
* 通過上下文自定義內部bean工廠。 嘗試呼叫每一個refresh 方法。
     * 預設實現應用此上下文setAllowBeanDefinitionOverriding 是否允許Bean定義重寫
     * 和 setAllowCircularReferences 是否允許迴圈依賴設定。
     * 如果特殊情況,可以在子類中重寫以任何自定義DefaultListableBeanFactory 設定。
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }
  1. 最關鍵的一步,載入Bean的定義資訊。所有關於Bean Definitions都會在這一步的這個方法進行解析。呼叫載入BeanDefinition的方法,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器AbstractXmlApplicationContext 中的loadBeanDefinitions方法,具體程式碼如下
// 通過XmlBeanDefinitionReader 載入bean 定義資訊。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  // 建立一個BeanDefinition閱讀器,通過閱讀XML檔案,真正完成BeanDefinition的載入和註冊
  XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

  // 配置bean 定義閱讀器和上下文資源載入環境。
  beanDefinitionReader.setEnvironment(this.getEnvironment());
  beanDefinitionReader.setResourceLoader(this);
  beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

  // 允許子類提供自定義初始化的reader,然後繼續載入bean定義資訊。
  initBeanDefinitionReader(beanDefinitionReader);

  // 委託給BeanDefinition閱讀器去載入BeanDefinition
  loadBeanDefinitions(beanDefinitionReader);
}
  • 首先來看第一步,建立XmlBeanDefinitionReader並進行BeanDefinition的載入和註冊。初始化XmlBeanDefinitionReader(beanFactory)構造方法,然後呼叫AbstractBeanDefinitionReader(BeanDefinitionRegistry registry)的構造方法,因為DefaultListableBeanFactory實現了BeanDefinitionRegistry介面,所以就把DefaultListableBeanFactory當作一個BeanDefinitionRegistry傳遞進去。下面看程式碼
/* 通過bean 工廠建立一個新的 AbstractBeanDefinitionReader。
     如果傳入的bean 工廠不僅實現了BeanDefinitionRegistry 介面還實現了ResourceLoader 介面。
   將會使用預設的ResourceLoader。 這是 ApplicationContext 實現的情況。
   如果給定一個普通的BeanDefinitionRegistry,則預設的 ResourceLoader 會是PathMatchingResourcePatternResolver
   如果傳入的bean 工廠實現了 EnvironmentCapable,則此讀取器將使用此環境。否則,這個讀取器
   將會初始化並且使用 StandardEnvironment。 所有ApplicationContext的實現都是 EnvironmentCapable,
     然而通常BeanFactory 的實現則不是。
 */
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
  ...
  if (this.registry instanceof ResourceLoader) {
    this.resourceLoader = (ResourceLoader) this.registry;
  }
  else {
    this.resourceLoader = new PathMatchingResourcePatternResolver();
  }
  if (this.registry instanceof EnvironmentCapable) {
    this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
  }
  else {
    this.environment = new StandardEnvironment();
  }
}

上面程式碼的意思就是,如果BeanDefinitionRegistry實現了ResourceLoader介面,就會強制轉換為ResourceLoader,否則,將會new 一個PathMatchingResourcePatternResolver(),如果實現了EnvironmentCapable介面,就直接獲取系統環境,否則就直接new一個StandardEnvironment。

  • 完成第一步之後,然後配置bean閱讀器和上下文資源載入環境,允許子類提供自定義初始化的reader,然後繼續載入bean定義資訊。這一步希望子類實現自定義的bean載入資訊。
  • 最後一步進行真正意義上當bean載入,委託給BeanDefinition閱讀器去載入BeanDefinition,來看具體的解析過程
// 委託給XmlBeanDefinition閱讀器去載入BeanDefinition
 // loadBeanDefinitions(beanDefinitionReader);  ↓
    
  @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        // 將讀入的XML資源進行特殊編碼處理
        return loadBeanDefinitions(new EncodedResource(resource));
    }

  // loadBeanDefinitions  ↓

    // loadBeanDefinitions(resources) 經過一系列的呼叫最終會呼叫到下面的程式碼
  // XmlBeanDefinitionReader 部分程式碼省略
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        ...
    // 從InputStream中得到XML的解析源
    InputSource inputSource = new InputSource(inputStream);
    if (encodedResource.getEncoding() != null) {
       inputSource.setEncoding(encodedResource.getEncoding());
    }
    // 這裡是具體的讀取過程
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
  
  // doLoadBeanDefinitions ↓
  protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
    
            // 通過DOM4J載入解析XML檔案,最終形成Document物件
            Document doc = doLoadDocument(inputSource, resource);
        // 通過對Document物件的操作,完成BeanDefinition的載入和註冊工作
        return registerBeanDefinitions(doc, resource);
    }
  
  // registerBeanDefinitions  ↓
  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 建立BeanDefinitionDocumentReader來解析Document物件,完成BeanDefinition解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 獲得容器中已經註冊的BeanDefinition數量
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 解析過程入口,BeanDefinitionDocumentReader只是個介面,具體的實現過程在DefaultBeanDefinitionDocumentReader完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        // 統計新的的BeanDefinition數量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

到這一步仍舊只是建立了各種需要解析的閱讀器物件,文件解析物件,解析之前的準備工作,

這裡說一下BeanDefinitionDocumentReader 這個介面:

/*
 * 每個要解析的文件例項化:實現可以在執行
 * registerBeanDefinitions 方法時儲存例項變數中的狀態
 * 例如,為文件中的所有bean定義定義的全域性設定。
*/
public interface BeanDefinitionDocumentReader {
  /*
  * 從 DOM 文件中讀取Bean 定義資訊並且通過閱讀器上下文環境把它們註冊進registry中
  */
  void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
            throws BeanDefinitionStoreException;
}

它的預設實現類只有一個,就是 DefaultBeanDefinitionDocumentReader ,此類負責做真正的bean 解析工作。

下面registerBeanDefinitions之後的方法會完成真正的bean解析工作:

DefaultBeanDefinitionDocumentReader.java

// 這個實現用來解析 spring-beans 約束
// 開啟DOM 文件,初始化預設<beans/>的設定;然後解析其中的bean定義資訊。
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  this.readerContext = readerContext;
  logger.debug("Loading bean definitions");
  // 獲得Document的根元素<beans>標籤
  Element root = doc.getDocumentElement();
  // 真正實現BeanDefinition解析和註冊工作
  doRegisterBeanDefinitions(root);
}

//      doRegisterBeanDefinitions(root);  ↓
protected void doRegisterBeanDefinitions(Element root) {

        /*
            任何巢狀的<beans>元素都將導致此方法中的遞迴。 為了正確傳播和保留<beans>default-* 屬性,跟蹤當前的
            父委託,可能為null。
            建立新的(子)委託,引用父項以進行回退,然後最終將this.delegate重置為其原始(父)引用。 此行為模擬了一堆代理,而實際上並不需要一個代理。
         */

        // 這裡使用了委託模式,將具體的BeanDefinition解析工作交給了BeanDefinitionParserDelegate去完成
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        // 判斷該根標籤是否包含http://www.springframework.org/schema/beans預設名稱空間
        ...
      
      
        // 在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性
        preProcessXml(root);
        // 委託給BeanDefinitionParserDelegate,從Document的根元素開始進行BeanDefinition的解析
        parseBeanDefinitions(root, this.delegate);
        // 在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性
        postProcessXml(root);

        this.delegate = parent;
    }

看到這裡已經真心不容易了,需要休息一下嗎?怎麼說呢,原始碼這個東西是需要耐住性子並且需要你投入大量精力的,看過一遍沒有印象太正常了,並且看原始碼的前提是沒人打擾,需要一個極度安靜的環境。

tips: 戴上耳機會好很多

在回到正題之前,我先來問你一個問題,你知道代理模式和委託模式的區別嗎?

我是這樣理解的

委託模式是自己不做這件事情呢,而是把事情交給別人去做
代理模式是讓別人搭把手,而自己是真正做這件事情的主角,因為代理實現類(實現了InvocationHandler 的類)只是個打嘴炮的人。

回到正題,在真正做解析工作的時候,會首先建立一個委託類BeanDefinitionParserDelegate ,那麼先來認識一下這個類。

// 用於解析XML bean定義的有狀態委託類。 旨在供主解析器和任何擴充套件使用
public class BeanDefinitionParserDelegate {
  
  // 此方法也就是委託給 BeanDefinitionParserDelegate 去做的事情
  protected BeanDefinitionParserDelegate createDelegate(
            XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {

        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }
}

它主要完成的事情在initDefaults 方法中,一起看下這個方法

/*
     * 初始化預設的 懶載入,自動注入,依賴檢查設定, 初始化方法,銷燬方法和合並設定。
     * 如果未在本地顯式設定預設值,則通過回退到給定父級來支援巢狀的“beans”元素用例。
     */
public void initDefaults(Element root, @Nullable BeanDefinitionParserDelegate parent) {
  populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
  this.readerContext.fireDefaultsRegistered(this.defaults);
}
/**
     * in case the defaults are not explicitly set locally.
     * 使用預設的 lazy-init,autowire,依賴檢查設定,init-method,destroy-method 和 合併設定 屬性 填充給定的
     * DocumentDefaultsDefinition,如果未在本地顯式設定預設值,則支援巢狀的'beans'元素用例,返回parentDefaults
     */
    protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
        // default-lazy-init
        String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
            // Potentially inherited from outer <beans> sections, otherwise falling back to false.
            lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
        }
        defaults.setLazyInit(lazyInit);

    // 餘下的程式碼和lazyinit 的邏輯相同,也是解析方法註釋中的各種屬性。 這裡礙於篇幅原因暫不貼出了
        ...
    //   
      
        defaults.setSource(this.readerContext.extractSource(root));
    }

然後方法一直返回到建立委託的入口也就是 BeanDefinitionParserDelegate parent = this.delegate;,然後是 Spring 留給開發人員的兩個介面,用於開發人員自己實現

...
// 在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性
preProcessXml(root);
// 委託給 BeanDefinitionParserDelegate,從Document的根元素開始進行BeanDefinition的解析
parseBeanDefinitions(root, this.delegate);
// 在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性
postProcessXml(root);

我們再來看一下 parseBeanDefinitions(root, this.delegate); 這個方法是交給剛剛建立的 BeanDefinitionParserDelegate 委託用來解析具體的 lazy-init 等屬性的標籤的

/**
     * 解析文件物件的根目錄節點: import, alias, bean
     */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  // 載入的Document物件是否使用了Spring預設的XML名稱空間(beans名稱空間)
  if (delegate.isDefaultNamespace(root)) {
    // 獲取Document物件根元素的所有子節點(bean標籤、import標籤、alias標籤和其他自定義標籤context、aop等)
    NodeList nl = root.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (node instanceof Element) {
        Element ele = (Element) node;
        // bean標籤、import標籤、alias標籤,則使用預設解析規則
        if (delegate.isDefaultNamespace(ele)) {
          parseDefaultElement(ele, delegate);
        }
        else {//像context標籤、aop標籤、tx標籤,則使用使用者自定義的解析規則解析元素節點
          delegate.parseCustomElement(ele);
        }
      }
    }
  }
  else {
    // 如果不是預設的名稱空間,則使用使用者自定義的解析規則解析元素節點
    delegate.parseCustomElement(root);
  }
}

上述步驟主要分為三步:

  • 先判斷是否委託類使用Spring預設的XML名稱空間(beans名稱空間),在判斷如果取出來的節點是bean、import、alias的話就使用預設的解析規則
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  // 解析<import>標籤
  if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
    importBeanDefinitionResource(ele);
  }
  // 解析<alias>標籤
  else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
    processAliasRegistration(ele);
  }
  // 解析<bean>標籤
  else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    processBeanDefinition(ele, delegate);
  }
  // 解析內建<bean>標籤
  else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    // recurse
    // 遞迴呼叫
    doRegisterBeanDefinitions(ele);
  }
}
  • 如果判斷取出來的不是預設節點,則會使用自定義的預設解析規則,context標籤,aop標籤, tx標籤則會預設使用此解析方法
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
  // 解析自定義標籤
  return parseCustomElement(ele, null);
}

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
  // 獲取名稱空間URI(就是獲取beans標籤的xmlns:aop或者xmlns:context屬性的值)
  String namespaceUri = getNamespaceURI(ele);
  if (namespaceUri == null) {
    return null;
  }
  // 根據不同的名稱空間URI,去匹配不同的NamespaceHandler(一個名稱空間對應一個NamespaceHandler)
  // 此處會呼叫DefaultNamespaceHandlerResolver類的resolve方法
  NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  if (handler == null) {
    error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
    return null;
  }
  // 呼叫匹配到的NamespaceHandler的解析方法
  return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
  • 如果不是使用 beans 的名稱空間,也會使用自定義的解析方法進行解析

然後方法解析完成,一直返回到 AbstractApplicationContext 中的 重新整理 bean Factory 的方法

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  // 主要是通過該方法完成IoC容器的重新整理
  // ClassPathXmlApplicationContext.refreshBeanFactory 呼叫的是 AbstractRefreshApplicationContext
  // AnnotationConfigApplicationContext.refreshBeanFactory呼叫的是GenericApplicationContext
  refreshBeanFactory();
  ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  if (logger.isDebugEnabled()) {
    logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  }
  return beanFactory;
}

這樣取出的 Bean 物件就可以進行使用了。

下一篇文章來 解析一下第三個步驟: 對IoC容器進行一些預處