1. 程式人生 > >最詳細的Spring核心IOC的原始碼分析(每次看都有不同的感悟)

最詳細的Spring核心IOC的原始碼分析(每次看都有不同的感悟)

Spring 最重要的概念是 IOC 和 AOP,本篇文章其實就是要帶領大家來分析下 Spring 的 IOC 容器。既然大家平時都要用到 Spring,怎麼可以不好好了解 Spring 呢?閱讀本文並不能讓你成為 Spring 專家,不過一定有助於大家理解 Spring 的很多概念,幫助大家排查應用中和 Spring 相關的一些問題。

閱讀建議:讀者至少需要知道怎麼配置 Spring,瞭解 Spring 中的各種概念,少部分內容我還假設讀者使用過 SpringMVC。本文要說的 IOC 總體來說有兩處地方最重要,一個是建立 Bean 容器,一個是初始化 Bean,如果讀者覺得一次性看完本文壓力有點大,那麼可以按這個思路分兩次消化。讀者不一定對 Spring 容器的原始碼感興趣,也許附錄部分介紹的知識對讀者有些許作用。

我採用的原始碼版本是 4.3.11.RELEASE,算是 5.0.x 前比較新的版本了。為了降低難度,本文所說的所有的內容都是基於 xml 的配置的方式,實際使用已經很少人這麼做了,至少不是純 xml 配置,不過從理解原始碼的角度來看用這種方式來說無疑是最合適的。如果讀者對註解方式的原始碼感興趣,也許等我有時間的時候可以寫篇文章介紹介紹。

我希望能將此文寫成一篇 Spring IOC 原始碼分析的好文章,希望通過本文可以讓讀者不懼怕閱讀 Spring 原始碼。

為了保持文章的嚴謹性,如果讀者發現我哪裡說錯了請一定不吝指出,非常希望可以聽到讀者的聲音。

目錄

引言

先看下最基本的啟動 Spring 容器的例子:

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
}

以上程式碼就可以利用配置檔案來啟動一個 Spring 容器了,請使用 maven 的小夥伴直接在 dependencies 中加上以下依賴即可,我比較反對那些不知道要新增什麼依賴,然後把 Spring 的所有相關的東西都加進來的方式。

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.11.RELEASE</version>
</dependency>

spring-context 會自動將 spring-core、spring-beans、spring-aop、spring-expression 這幾個基礎 jar 包帶進來。

多說一句,很多開發者入門就直接接觸的 SpringMVC,對 Spring 其實不是很瞭解,Spring 是漸進式的工具,並不具有很強的侵入性,它的模組也劃分得很合理,即使你的應用不是 web 應用,或者之前完全沒有使用到 Spring,而你就想用 Spring 的依賴注入這個功能,其實完全是可以的,它的引入不會對其他的元件產生衝突。

廢話說完,我們繼續。ApplicationContext context = new ClassPathXmlApplicationContext(...) 其實很好理解,從名字上就可以猜出一二,就是在 ClassPath 中尋找 xml 配置檔案,根據 xml 檔案內容來構建 ApplicationContext。當然,除了 ClassPathXmlApplicationContext 以外,我們也還有其他構建 ApplicationContext 的方案可供選擇,我們先來看看大體的繼承結構是怎麼樣的:

1

讀者可以大致看一下類名,原始碼分析的時候不至於找不著看哪個類,因為 Spring 為了適應各種使用場景,提供的各個介面都可能有很多的實現類。對於我們來說,就是揪著一個完整的分支看完。當然,讀本文的時候讀者也不必太擔心,每個程式碼塊分析的時候,我都會告訴讀者我們在說哪個類第幾行。

我們可以看到,ClassPathXmlApplicationContext 兜兜轉轉了好久才到 ApplicationContext 介面,同樣的,我們也可以使用綠顏色的 FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext 這兩個類。

FileSystemXmlApplicationContext 的建構函式需要一個 xml 配置檔案在系統中的路徑,其他和 ClassPathXmlApplicationContext 基本上一樣。

AnnotationConfigApplicationContext 是基於註解來使用的,它不需要配置檔案,採用 java 配置類和各種註解來配置,是比較簡單的方式,也是大勢所趨吧。

不過本文旨在幫助大家理解整個構建流程,所以決定使用 ClassPathXmlApplicationContext 進行分析。

我們先來一個簡單的例子來看看怎麼例項化 ApplicationContext。

首先,定義一個介面:

public interface MessageService {
    String getMessage();
}

定義介面實現類:

public class MessageServiceImpl implements MessageService {

    public String getMessage() {
        return "hello world";
    }
}

接下來,我們在 resources 目錄新建一個配置檔案,檔名隨意,通常叫 application.xml 或 application-xxx.xml就可以了:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">

    <bean id="messageService" class="com.javadoop.example.MessageServiceImpl"/>
</beans>

這樣,我們就可以跑起來了:

public class App {
    public static void main(String[] args) {
        // 用我們的配置檔案來啟動一個 ApplicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");

        System.out.println("context 啟動成功");

        // 從 context 中取出我們的 Bean,而不是用 new MessageServiceImpl() 這種方式
        MessageService messageService = context.getBean(MessageService.class);
        // 這句將輸出: hello world
        System.out.println(messageService.getMessage());
    }
}

以上例子很簡單,不過也夠引出本文的主題了,就是怎麼樣通過配置檔案來啟動 Spring 的 ApplicationContext?也就是我們今天要分析的 IOC 的核心了。ApplicationContext 啟動過程中,會負責建立例項 Bean,往各個 Bean 中注入依賴等。

BeanFactory 簡介

初學者可別以為我之前說那麼多和 BeanFactory 無關,前面說的 ApplicationContext 其實就是一個 BeanFactory。我們來看下和 BeanFactory 介面相關的主要的繼承結構:

2

我想,大家看完這個圖以後,可能就不是很開心了。ApplicationContext 往下的繼承結構前面一張圖說過了,這裡就不重複了。這張圖呢,背下來肯定是不需要的,有幾個重點和大家說明下就好。

  1. ApplicationContext 繼承了 ListableBeanFactory,這個 Listable 的意思就是,通過這個介面,我們可以獲取多個 Bean,最頂層 BeanFactory 介面的方法都是獲取單個 Bean 的。
  2. ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞本身已經能說明問題了,也就是說我們可以在應用中起多個 BeanFactory,然後可以將各個 BeanFactory 設定為父子關係。
  3. AutowireCapableBeanFactory 這個名字中的 Autowire 大家都非常熟悉,它就是用來自動裝配 Bean 用的,但是仔細看上圖,ApplicationContext 並沒有繼承它,不過不用擔心,不使用繼承,不代表不可以使用組合,如果你看到 ApplicationContext 介面定義中的最後一個方法 getAutowireCapableBeanFactory() 就知道了。
  4. ConfigurableListableBeanFactory 也是一個特殊的介面,看圖,特殊之處在於它繼承了第二層所有的三個介面,而 ApplicationContext 沒有。這點之後會用到。
  5. 請先不用花時間在其他的介面和類上,先理解我說的這幾點就可以了。

然後,請讀者開啟編輯器,翻一下 BeanFactory、ListableBeanFactory、HierarchicalBeanFactory、AutowireCapableBeanFactory、ApplicationContext 這幾個介面的程式碼,大概看一下各個介面中的方法,大家心裡要有底,限於篇幅,我就不貼程式碼介紹了。

啟動過程分析

下面將會是冗長的程式碼分析,請讀者先喝個水。記住,一定要在電腦中開啟原始碼,不然純看是很累的。

第一步,我們肯定要從 ClassPathXmlApplicationContext 的構造方法說起。

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  private Resource[] configResources;

  // 如果已經有 ApplicationContext 並需要配置成父子關係,那麼呼叫這個構造方法
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  ...
  public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
      throws BeansException {

    super(parent);
    // 根據提供的路徑,處理成配置檔案陣列(以分號、逗號、空格、tab、換行符分割)
    setConfigLocations(configLocations);
    if (refresh) {
      refresh(); // 核心方法
    }
  }
    ...
}

接下來,就是 refresh(),這裡簡單說下為什麼是 refresh(),而不是 init() 這種名字的方法。因為 ApplicationContext 建立起來以後,其實我們是可以通過呼叫 refresh() 這個方法重建的,這樣會將原來的 ApplicationContext 銷燬,然後再重新執行一次初始化操作。

往下看,refresh() 方法裡面呼叫了那麼多方法,就知道肯定不簡單了,請讀者先看個大概,細節之後會詳細說。

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 來個鎖,不然 refresh() 還沒結束,你又來個啟動或銷燬容器的操作,那不就亂套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 準備工作,記錄下容器的啟動時間、標記“已啟動”狀態、處理配置檔案中的佔位符
      prepareRefresh();

      // 這步比較關鍵,這步完成後,配置檔案就會解析成一個個 Bean 定義,註冊到 BeanFactory 中,
      // 當然,這裡說的 Bean 還沒有初始化,只是配置資訊都提取出來了,
      // 註冊也只是將這些資訊都儲存到了註冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設定 BeanFactory 的類載入器,新增幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
      // 這塊待會會展開說
      prepareBeanFactory(beanFactory);

      try {
         // 【這裡需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現了此介面,
         // 那麼在容器初始化以後,Spring 會負責呼叫裡面的 postProcessBeanFactory 方法。】

         // 這裡是提供給子類的擴充套件點,到這裡的時候,所有的 Bean 都載入、註冊完成了,但是都還沒有初始化
         // 具體的子類可以在這步的時候新增一些特殊的 BeanFactoryPostProcessor 的實現類或做點什麼事
         postProcessBeanFactory(beanFactory);
         // 呼叫 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 註冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
         // 此介面兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個方法分別在 Bean 初始化之前和初始化之後得到執行。注意,到這裡 Bean 還沒初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化當前 ApplicationContext 的 MessageSource,國際化這裡就不展開說了,不然沒完沒了了
         initMessageSource();

         // 初始化當前 ApplicationContext 的事件廣播器,這裡也不展開了
         initApplicationEventMulticaster();

         // 從方法名就可以知道,典型的模板方法(鉤子方法),
         // 具體的子類可以在這裡初始化一些特殊的 Bean(在初始化 singleton beans 之前)
         onRefresh();

         // 註冊事件監聽器,監聽器需要實現 ApplicationListener 介面。這也不是我們的重點,過
         registerListeners();

         // 重點,重點,重點
         // 初始化所有的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最後,廣播事件,ApplicationContext 初始化完成
         finishRefresh();
      }

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

         // Destroy already created singletons to avoid dangling resources.
         // 銷燬已經初始化的 singleton 的 Beans,以免有些 bean 會一直佔用資源
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // 把異常往外拋
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

下面,我們開始一步步來肢解這個 refresh() 方法。

建立 Bean 容器前的準備工作

這個比較簡單,直接看程式碼中的幾個註釋即可。

protected void prepareRefresh() {
   // 記錄啟動時間,
   // 將 active 屬性設定為 true,closed 屬性設定為 false,它們都是 AtomicBoolean 型別
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   initPropertySources();

   // 校驗 xml 配置檔案
   getEnvironment().validateRequiredProperties();

   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

建立 Bean 容器,載入並註冊 Bean

注意,這個方法是全文最重要的部分之一,這裡將會初始化 BeanFactory、載入 Bean、註冊 Bean 等等。

當然,這步結束後,Bean 並沒有完成初始化。

// AbstractApplicationContext.java

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // 關閉舊的 BeanFactory (如果有),建立新的 BeanFactory,載入 Bean 定義、註冊 Bean 等等
   refreshBeanFactory();

   // 返回剛剛建立的 BeanFactory
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

// AbstractRefreshableApplicationContext.java 120

@Override
protected final void refreshBeanFactory() throws BeansException {
   // 如果 ApplicationContext 中已經載入過 BeanFactory 了,銷燬所有 Bean,關閉 BeanFactory
   // 注意,應用中 BeanFactory 本來就是可以多個的,這裡可不是說應用全域性是否有 BeanFactory,而是當前
   // ApplicationContext 是否有 BeanFactory
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // 初始化一個 DefaultListableBeanFactory,為什麼用這個,我們馬上說。
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // 用於 BeanFactory 的序列化,我想不部分人應該都用不到
      beanFactory.setSerializationId(getId());

      // 下面這兩個方法很重要,別跟丟了,具體細節之後說
      // 設定 BeanFactory 的兩個配置屬性:是否允許 Bean 覆蓋、是否允許迴圈引用
      customizeBeanFactory(beanFactory);

      // 載入 Bean 到 BeanFactory 中
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

看到這裡的時候,我覺得讀者就應該站在高處看 ApplicationContext 了,ApplicationContext 繼承自 BeanFactory,但是它不應該被理解為 BeanFactory 的實現類,而是說其內部持有一個例項化的 BeanFactory(DefaultListableBeanFactory)。以後所有的 BeanFactory 相關的操作其實是給這個例項來處理的。

我們說說為什麼選擇例項化 DefaultListableBeanFactory ?前面我們說了有個很重要的介面 ConfigurableListableBeanFactory,它實現了 BeanFactory 下面一層的所有三個介面,我把之前的繼承圖再拿過來大家再仔細看一下:

3

我們可以看到 ConfigurableListableBeanFactory 只有一個實現類 DefaultListableBeanFactory,而且實現類 DefaultListableBeanFactory 還通過實現右邊的 AbstractAutowireCapableBeanFactory 通吃了右路。所以結論就是,最底下這個傢伙 DefaultListableBeanFactory 基本上是最牛的 BeanFactory 了,這也是為什麼這邊會使用這個類來例項化的原因。

在繼續往下之前,我們需要先了解 BeanDefinition。我們說 BeanFactory 是 Bean 容器,那麼 Bean 又是什麼呢?

這裡的 BeanDefinition 就是我們所說的 Spring 的 Bean,我們自己定義的各個 Bean 其實會轉換成一個個 BeanDefinition 存在於 Spring 的 BeanFactory 中。

所以,如果有人問你 Bean 是什麼的時候,你要知道 Bean 在程式碼層面上是 BeanDefinition 的例項。

BeanDefinition 中儲存了我們的 Bean 資訊,比如這個 Bean 指向的是哪個類、是否是單例的、是否懶載入、這個 Bean 依賴了哪些 Bean 等等。

BeanDefinition 介面定義

我們來看下 BeanDefinition 的介面定義:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

   // 我們可以看到,預設只提供 sington 和 prototype 兩種,
   // 很多讀者都知道還有 request, session, globalSession, application, websocket 這幾種,
   // 不過,它們屬於基於 web 的擴充套件。
   String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
   String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

   // 比較不重要,直接跳過吧
   int ROLE_APPLICATION = 0;
   int ROLE_SUPPORT = 1;
   int ROLE_INFRASTRUCTURE = 2;

   // 設定父 Bean,這裡涉及到 bean 繼承,不是 java 繼承。請參見附錄介紹
   void setParentName(String parentName);

   // 獲取父 Bean
   String getParentName();

   // 設定 Bean 的類名稱
   void setBeanClassName(String beanClassName);

   // 獲取 Bean 的類名稱
   String getBeanClassName();


   // 設定 bean 的 scope
   void setScope(String scope);

   String getScope();

   // 設定是否懶載入
   void setLazyInit(boolean lazyInit);

   boolean isLazyInit();

   // 設定該 Bean 依賴的所有的 Bean,注意,這裡的依賴不是指屬性依賴(如 @Autowire 標記的),
   // 是 depends-on="" 屬性設定的值。
   void setDependsOn(String... dependsOn);

   // 返回該 Bean 的所有依賴
   String[] getDependsOn();

   // 設定該 Bean 是否可以注入到其他 Bean 中,只對根據型別注入有效,
   // 如果根據名稱注入,即使這邊設定了 false,也是可以的
   void setAutowireCandidate(boolean autowireCandidate);

   // 該 Bean 是否可以注入到其他 Bean 中
   boolean isAutowireCandidate();

   // 主要的。同一介面的多個實現,如果不指定名字的話,Spring 會優先選擇設定 primary 為 true 的 bean
   void setPrimary(boolean primary);

   // 是否是 primary 的
   boolean isPrimary();

   // 如果該 Bean 採用工廠方法生成,指定工廠名稱。對工廠不熟悉的讀者,請參加附錄
   void setFactoryBeanName(String factoryBeanName);
   // 獲取工廠名稱
   String getFactoryBeanName();
   // 指定工廠類中的 工廠方法名稱
   void setFactoryMethodName(String factoryMethodName);
   // 獲取工廠類中的 工廠方法名稱
   String getFactoryMethodName();

   // 構造器引數
   ConstructorArgumentValues getConstructorArgumentValues();

   // Bean 中的屬性值,後面給 bean 注入屬性值的時候會說到
   MutablePropertyValues getPropertyValues();

   // 是否 singleton
   boolean isSingleton();

   // 是否 prototype
   boolean isPrototype();

   // 如果這個 Bean 原生是抽象類,那麼不能例項化
   boolean isAbstract();

   int getRole();
   String getDescription();
   String getResourceDescription();
   BeanDefinition getOriginatingBeanDefinition();
}

這個 BeanDefinition 其實已經包含很多的資訊了,暫時不清楚所有的方法對應什麼東西沒關係,希望看完本文後讀者可以徹底搞清楚裡面的所有東西。

這裡介面雖然那麼多,但是沒有類似 getInstance() 這種方法來獲取我們定義的類的例項,真正的我們定義的類生成的例項到哪裡去了呢?彆著急,這個要很後面才能講到。

有了 BeanDefinition 的概念以後,我們再往下看 refreshBeanFactory() 方法中的剩餘部分:

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);

雖然只有兩個方法,但路還很長啊。。。

customizeBeanFactory

customizeBeanFactory(beanFactory) 比較簡單,就是配置是否允許 BeanDefinition 覆蓋、是否允許迴圈引用。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   if (this.allowBeanDefinitionOverriding != null) {
      // 是否允許 Bean 定義覆蓋
      beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.allowCircularReferences != null) {
      // 是否允許 Bean 間的迴圈依賴
      beanFactory.setAllowCircularReferences(this.allowCircularReferences);
   }
}

BeanDefinition 的覆蓋問題大家也許會碰到,就是在配置檔案中定義 bean 時使用了相同的 id 或 name,預設情況下,allowBeanDefinitionOverriding 屬性為 null,如果在同一配置檔案中重複了,會拋錯,但是如果不是同一配置檔案中,會發生覆蓋。

迴圈引用也很好理解:A 依賴 B,而 B 依賴 A。或 A 依賴 B,B 依賴 C,而 C 依賴 A。

預設情況下,Spring 允許迴圈依賴,當然如果你在 A 的構造方法中依賴 B,在 B 的構造方法中依賴 A 是不行的。

至於這兩個屬性怎麼配置?我在附錄中進行了介紹,尤其對於覆蓋問題,很多人都希望禁止出現 Bean 覆蓋,可是 Spring 預設是不同檔案的時候可以覆蓋的。

之後的原始碼中還會出現這兩個屬性,讀者有個印象就可以了。

載入 Bean: loadBeanDefinitions

接下來是最重要的 loadBeanDefinitions(beanFactory) 方法了,這個方法將根據配置,載入各個 Bean,然後放到 BeanFactory 中。

讀取配置的操作在 XmlBeanDefinitionReader 中,其負責載入配置、解析。

// AbstractXmlApplicationContext.java 80

/** 我們可以看到,此方法將通過一個 XmlBeanDefinitionReader 例項來載入各個 Bean。*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 給這個 BeanFactory 例項化一個 XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // 初始化 BeanDefinitionReader,其實這個是提供給子類覆寫的,
   // 我看了一下,沒有類覆寫這個方法,我們姑且當做不重要吧
   initBeanDefinitionReader(beanDefinitionReader);
   // 重點來了,繼續往下
   loadBeanDefinitions(beanDefinitionReader);
}

現在還在這個類中,接下來用剛剛初始化的 Reader 開始來載入 xml 配置,這塊程式碼讀者可以選擇性跳過,不是很重要。也就是說,下面這個程式碼塊,讀者可以很輕鬆地略過。

// AbstractXmlApplicationContext.java 120

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      // 往下看
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

// 上面雖然有兩個分支,不過第二個分支很快通過解析路徑轉換為 Resource 以後也會進到這裡
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   // 注意這裡是個 for 迴圈,也就是每個檔案是一個 resource
   for (Resource resource : resources) {
      // 繼續往下看
      counter += loadBeanDefinitions(resource);
   }
   return counter;
}

// XmlBeanDefinitionReader 303
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}

// XmlBeanDefinitionReader 314
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from " + encodedResource.getResource());
   }
   // 用一個 ThreadLocal 來存放所有的配置檔案資源
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<EncodedResource>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         // 核心部分
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
         inputStream.close();
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

// 還在這個檔案中,第 388 行
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // 這裡就不看了
      Document doc = doLoadDocument(inputSource, resource);
      // 繼續
      return registerBeanDefinitions(doc, resource);
   }
   catch (...
}
// 還在這個檔案中,第 505 行
// 返回從當前配置檔案載入了多少數量的 Bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   int countBefore = getRegistry().getBeanDefinitionCount();
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}
// DefaultBeanDefinitionDocumentReader 90
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   logger.debug("Loading bean definitions");
   Element root = doc.getDocumentElement();
   doRegisterBeanDefinitions(root);
}

經過漫長的鏈路,一個配置檔案終於轉換為一顆 DOM 樹了,注意,這裡指的是其中一個配置檔案,不是所有的,讀者可以看到上面有個 for 迴圈的。下面從根節點開始解析:

doRegisterBeanDefinitions:

// DefaultBeanDefinitionDocumentReader 116
protected void doRegisterBeanDefinitions(Element root) {
   // 我們看名字就知道,BeanDefinitionParserDelegate 必定是一個重要的類,它負責解析 Bean 定義,
   // 這裡為什麼要定義一個 parent? 看到後面就知道了,是遞迴問題,
   // 因為 <beans /> 內部是可以定義 <beans /> 的,所以這個方法的 root 其實不一定就是 xml 的根節點,也可以是巢狀在裡面的 <beans /> 節點,從原始碼分析的角度,我們當做根節點就好了
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);

   if (this.delegate.isDefaultNamespace(root)) {
      // 這塊說的是根節點 <beans ... profile="dev" /> 中的 profile 是否是當前環境需要的,
      // 如果當前環境配置的 profile 不包含此 profile,那就直接 return 了,不對此 <beans /> 解析
      // 不熟悉 profile 為何物,不熟悉怎麼配置 profile 讀者的請移步附錄區
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isInfoEnabled()) {
               logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }

   preProcessXml(root); // 鉤子
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root); // 鉤子

   this.delegate = parent;
}

preProcessXml(root) 和 postProcessXml(root) 是給子類用的鉤子方法,鑑於沒有被使用到,也不是我們的重點,我們直接跳過。

這裡涉及到了 profile 的問題,對於不瞭解的讀者,我在附錄中對 profile 做了簡單的解釋,讀者可以參考一下。

接下來,看核心解析方法 parseBeanDefinitions(root, this.delegate) :

// default namespace 涉及到的就四個標籤 <import />、<alias />、<bean /> 和 <beans />,
// 其他的屬於 custom 的
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   if (delegate.isDefaultNamespace(root)) {
      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;
            if (delegate.isDefaultNamespace(ele)) {
               parseDefaultElement(ele, delegate);
            }
            else {
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

從上面的程式碼,我們可以看到,對於每個配置來說,分別進入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 這兩個分支了。

parseDefaultElement(ele, delegate) 代表解析的節點是 <import /><alias /><bean /><beans /> 這幾個。

這裡的四個標籤之所以是 default 的,是因為它們是處於這個 namespace 下定義的:

http://www.springframework.org/schema/beans

又到初學者科普時間,不熟悉 namespace 的讀者請看下面貼出來的 xml,這裡的第二行 xmlns 就是咯。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">

而對於其他的標籤,將進入到 delegate.parseCustomElement(element) 這個分支。如我們經常會使用到的 <mvc /><task /><context /><aop />等。

這些屬於擴充套件,如果需要使用上面這些 ”非 default“ 標籤,那麼上面的 xml 頭部的地方也要引入相應的 namespace 和 .xsd 檔案的路徑,如下所示。同時代碼中需要提供相應的 parser 來解析,如 MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler 等。

假如讀者想分析 <context:property-placeholder location="classpath:xx.properties" /> 的實現原理,就應該到 ContextNamespaceHandler 中找答案。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xsi:schemaLocation="
           http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc   
           http://www.springframework.org/schema/mvc/spring-mvc.xsd  
       "
      default-autowire="byName">

回過神來,看看處理 default 標籤的方法:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      // 處理 <import /> 標籤
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      // 處理 <alias /> 標籤定義
      // <alias name="fromName" alias="toName"/>
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      // 處理 <bean /> 標籤定義,這也算是我們的重點吧
      processBeanDefinition(ele, delegate);
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // 如果碰到的是巢狀的 <beans /> 標籤,需要遞迴
      doRegisterBeanDefinitions(ele);
   }
}

如果每個標籤都說,那我不吐血,你們都要吐血了。我們挑我們的重點 <bean /> 標籤出來說。

processBeanDefinition

下面是 processBeanDefinition 解析 <bean /> 標籤:

// DefaultBeanDefinitionDocumentReader 298

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 將 <bean /> 節點中的資訊提取出來,然後封裝到一個 BeanDefinitionHolder 中,細節往下看
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

   // 下面的幾行先不要看,跳過先,跳過先,跳過先,後面會繼續說的

   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

繼續往下看怎麼解析之前,我們先看下 <bean /> 標籤中可以定義哪些屬性:

Property
class 類的全限定名
name 可指定 id、name(用逗號、分號、空格分隔)
scope 作用域
constructor arguments 指定構造引數
properties 設定屬性的值
autowiring mode no(預設值)、byName、byType、 constructor
lazy-initialization mode 是否懶載入(如果被非懶載入的bean依賴了那麼其實也就不能懶載入了)
initialization method bean 屬性設定完成後,會呼叫這個方法
destruction method bean 銷燬後的回撥方法

上面表格中的內容我想大家都非常熟悉吧,如果不熟悉,那就是你不夠了解 Spring 的配置了。

簡單地說就是像下面這樣子:

<bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean"
      scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup">

    <!-- 可以用下面三種形式指定構造引數 -->
  <constructor-arg type="int" value="7500000"/>
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg index="0" value="7500000"/>

    <!-- property 的幾種情況 -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

當然,除了上面舉例出來的這些,還有 factory-bean、factory-method、<lockup-method /><replaced-method /><meta /><qualifier /> 這幾個,大家是不是熟悉呢?

有了以上這些知識以後,我們再繼續往裡看怎麼解析 bean 元素,是怎麼轉換到 BeanDefinitionHolder 的。

// BeanDefinitionParserDelegate 428

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
   String id = ele.getAttribute(ID_ATTRIBUTE);
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

   List<String> aliases = new ArrayList<String>();

   // 將 name 屬性的定義按照 ”逗號、分號、空格“ 切分,形成一個別名列表陣列,
   // 當然,如果你不定義的話,就是空的了
   // 我在附錄中簡單介紹了一下 id 和 name 的配置,大家可以看一眼,有個20秒就可以了
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   String beanName = id;
   // 如果沒有指定id, 那麼用別名列表的第一個名字作為beanName
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
      if (logger.isDebugEnabled()) {
         logger.debug("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases");
      }
   }

   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }

   // 根據 <bean ...>...</bean> 中的配置建立 BeanDefinition,然後把配置中的資訊都設定到例項中,
   // 細節後面再說
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

   // 到這裡,整個 <bean /> 標籤就算解析結束了,一個 BeanDefinition 就形成了。
   if (beanDefinition != null) {
      // 如果都沒有設定 id 和 name,那麼此時的 beanName 就會為 null,進入下面這塊程式碼產生
      // 如果讀者不感興趣的話,我覺得不需要關心這塊程式碼,對本文原始碼分析來說,這些東西不重要
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {// 按照我們的思路,這裡 containingBean 是 null 的
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               // 如果我們不定義 id 和 name,那麼我們引言裡的那個例子:
               //   1. beanName 為:com.javadoop.example.MessageServiceImpl#0
               //   2. beanClassName 為:com.javadoop.example.MessageServiceImpl

               beanName = this.readerContext.generateBeanName(beanDefinition);

               String beanClassName = beanDefinition.getBeanClassName();
               if (beanClassName != null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                  // 把 beanClassName 設定為 Bean 的別名
                  aliases.add(beanClassName);
               }
            }
            if (logger.isDebugEnabled()) {
               logger.debug("Neither XML 'id' nor 'name' specified - " +
                     "using generated bean name [" + beanName + "]");
            }
         }
         catch (Exception ex) {
            error(ex.getMessage(), ele);
            return null;
         }
      }
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      // 返回 BeanDefinitionHolder
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}

看看怎麼根據配置建立 BeanDefinition:

public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, BeanDefinition containingBean) {

   this.parseState.push(new BeanEntry(beanName));

   String className = null;
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }

   try {
      String parent = null;
      if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
         parent = ele.getAttribute(PARENT_ATTRIBUTE);
      }
      // 建立 BeanDefinition,然後設定類資訊而已,很簡單,就不貼程式碼了
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);

      // 設定 BeanDefinition 的一堆屬性,這些屬性定義在 AbstractBeanDefinition 中
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

      /**
       * 下面的一堆是解析 <bean>......</bean> 內部的子元素,
       * 解析出來以後的資訊都放到 bd 的屬性中
       */

      // 解析 <meta />
      parseMetaElements(ele, bd);
      // 解析 <lookup-method />
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // 解析 <replaced-method />
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    // 解析 <constructor-arg />
      parseConstructorArgElements(ele, bd);
      // 解析 <property />
      parsePropertyElements(ele, bd);
      // 解析 <qualifier />
      parseQualifierElements(ele, bd);

      bd.setResource(this.readerContext.getResource());
      bd.setSource(extractSource(ele));

      return bd;
   }
   catch (ClassNotFoundException ex) {
      error("Bean class [" + className + "] not found", ele, ex);
   }
   catch (NoClassDefFoundError err) {
      error("Class that bean class [" + className + "] depends on not found", ele, err);
   }
   catch (Throwable ex) {
      error("Unexpected failure during bean definition parsing", ele, ex);
   }
   finally {
      this.parseState.pop();
   }

   return null;
}

到這裡,我們已經完成了根據 <bean /> 配置建立了一個 BeanDefinitionHolder 例項。注意,是一個。

我們回到解析 <bean /> 的入口方法:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 將 <bean /> 節點轉換為 BeanDefinitionHolder,就是上面說的一堆
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      // 如果有自定義屬性的話,進行相應的解析,先忽略
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // 我們把這步叫做 註冊Bean 吧
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // 註冊完成後,傳送事件,本文不展開說這個
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

大家再仔細看一下這塊吧,我們後面就不回來說這個了。這裡已經根據一個 <bean /> 標籤產生了一個 BeanDefinitionHolder 的例項,這個例項裡面也就是一個 BeanDefinition 的例項和它的 beanName、aliases 這三個資訊,注意,我們的關注點始終在 BeanDefinition 上:

public class BeanDefinitionHolder implements BeanMetadataElement {

  private final BeanDefinition beanDefinition;

  private final String beanName;

  private final String[] aliases;
...

然後我們準備註冊這個 BeanDefinition,最後,把這個註冊事件傳送出去。

下面,我們開始說註冊 Bean 吧。

註冊 Bean

// BeanDefinitionReaderUtils 143

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   String beanName = definitionHolder.getBeanName();
   // 註冊這個 Bean
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // 如果還有別名的話,也要根據別名統統註冊一遍,不然根據別名就找不到 Bean 了,這我們就不開心了
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         // alias -> beanName 儲存它們的別名資訊,這個很簡單,用一個 map 儲存一下就可以了,
         // 獲取的時候,會先將 alias 轉換為 beanName,然後再查詢
         registry.registerAlias(beanName, alias);
      }
   }
}

別名註冊的放一邊,畢竟它很簡單,我們看看怎麼註冊 Bean。

// DefaultListableBeanFactory 793

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(...);
      }
   }

   // old? 還記得 “允許 bean 覆蓋” 這個配置嗎?allowBeanDefinitionOverriding
   BeanDefinition oldBeanDefinition;

   // 之後會看到,所有的 Bean 註冊後會放入這個 beanDefinitionMap 中
   oldBeanDefinition = this.beanDefinitionMap.get(beanName);

   // 處理重複名稱的 Bean 定義的情況
   if (oldBeanDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
         // 如果不允許覆蓋的話,拋異常
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription()...
      }
      else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
         // log...用框架定義的 Bean 覆蓋使用者自定義的 Bean 
      }
      else if (!beanDefinition.equals(oldBeanDefinition)) {
         // log...用新的 Bean 覆蓋舊的 Bean
      }
      else {
         // log...用同等的 Bean 覆蓋舊的 Bean,這裡指的是 equals 方法返回 true 的 Bean
      }
      // 覆蓋
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      // 判斷是否已經有其他的 Bean 開始初始化了.
      // 注意,"註冊Bean" 這個動作結束,Bean 依然還沒有初始化,我們後面會有大篇幅說初始化過程,
      // 在 Spring 容器啟動的最後,會 預初始化 所有的 singleton beans
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons;
            }
         }
      }
      else {
         // 最正常的應該是進到這裡。

         // 將 BeanDefinition 放到這個 map 中,這個 map 儲存了所有的 BeanDefinition
         this.beanDefinitionMap.put(beanName, beanDefinition);
         // 這是個 ArrayList,所以會按照 bean 配置的順序儲存每一個註冊的 Bean 的名字
         this.beanDefinitionNames.add(beanName);
         // 這是個 LinkedHashSet,代表的是手動註冊的 singleton bean,
         // 注意這裡是 remove 方法,到這裡的 Bean 當然不是手動註冊的
         // 手動指的是通過呼叫以下方法註冊的 bean :
         //     registerSingleton(String beanName, Object singletonObject)
         //         這不是重點,解釋只是為了不讓大家疑惑。Spring 會在後面"手動"註冊一些 Bean,如 "environment"、"systemProperties" 等 bean
         this.manualSingletonNames.remove(beanName);
      }
      // 這個不重要,在預初始化的時候會用到,不必管它。
      this.frozenBeanDefinitionNames = null;
   }

   if (oldBeanDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}

總結一下,到這裡已經初始化了 Bean 容器,<bean /> 配置也相應的轉換為了一個個 BeanDefinition,然後註冊了各個 BeanDefinition 到註冊中心,並且傳送了註冊事件。

Bean 容器例項化完成後

說到這裡,我們回到 refresh() 方法,我重新貼了一遍程式碼,看看我們說到哪了。是的,我們才說完 obtainFreshBeanFactory() 方法。

考慮到篇幅,這裡開始大幅縮減掉沒必要詳細介紹的部分,大家直接看下面的程式碼中的註釋就好了。

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 來個鎖,不然 refresh() 還沒結束,你又來個啟動或銷燬容器的操作,那不就亂套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 準備工作,記錄下容器的啟動時間、標記“已啟動”狀態、處理配置檔案中的佔位符
      prepareRefresh();

      // 這步比較關鍵,這步完成後,配置檔案就會解析成一個個 Bean 定義,註冊到 BeanFactory 中,
      // 當然,這裡說的 Bean 還沒有初始化,只是配置資訊都提取出來了,
      // 註冊也只是將這些資訊都儲存到了註冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設定 BeanFactory 的類載入器,新增幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
      // 這塊待會會展開說
      prepareBeanFactory(beanFactory);

      try {
         // 【這裡需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現了此介面,
         // 那麼在容器初始化以後,Spring 會負責呼叫裡面的 postProcessBeanFactory 方法。】

         // 這裡是提供給子類的擴充套件點,到這裡的時候,所有的 Bean 都載入、註冊完成了,但是都還沒有初始化
         // 具體的子類可以在這步的時候新增一些特殊的 BeanFactoryPostProcessor 的實現類或做點什麼事
         postProcessBeanFactory(beanFactory);
         // 呼叫 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 註冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
         // 此介面兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個方法分別在 Bean 初始化之前和初始化之後得到執行。注意,到這裡 Bean 還沒初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化當前 ApplicationContext 的 MessageSource,國際化這裡就不展開說了,不然沒完沒了了
         initMessageSource();

         // 初始化當前 ApplicationContext 的事件廣播器,這裡也不展開了
         initApplicationEventMulticaster();

         // 從方法名就可以知道,典型的模板方法(鉤子方法),
         // 具體的子類可以在這裡初始化一些特殊的 Bean(在初始化 singleton beans 之前)
         onRefresh();

         // 註冊事件監聽器,監聽器需要實現 ApplicationListener 介面。這也不是我們的重點,過
         registerListeners();

         // 重點,重點,重點
         // 初始化所有的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最後,廣播事件,ApplicationContext 初始化完成
         finishRefresh();
      }

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

         // Destroy already created singletons to avoid dangling resources.
         // 銷燬已經初始化的 singleton 的 Beans,以免有些 bean 會一直佔用資源
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // 把異常往外拋
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

準備 Bean 容器: prepareBeanFactory

這裡簡單介紹下 prepareBeanFactory(factory) 方法:

/**
 * Configure the factory's standard context characteristics,
 * such as the context's ClassLoader and post-processors.
 * @param beanFactory the BeanFactory to configure
 */
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   // 設定 BeanFactory 的類載入器,我們知道 BeanFactory 需要載入類,也就需要類載入器,
   // 這裡設定為當前 ApplicationContext 的類載入器
   beanFactory.setBeanClassLoader(getClassLoader());
   // 設定 BeanExpressionResolver
   beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
   // 
   beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

   // 新增一個 BeanPostProcessor,這個 processor 比較簡單,
   // 實現了 Aware 介面的幾個特殊的 beans 在初始化的時候,這個 processor 負責回撥
   beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

   // 下面幾行的意思就是,如果某個 bean 依賴於以下幾個介面的實現類,在自動裝配的時候忽略它們,
   // Spring 會通過其他方式來處理這些依賴。
   beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
   beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
   beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
   beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

   /**
    * 下面幾行就是為特殊的幾個 bean 賦值,如果有 bean 依賴了以下幾個,會注入這邊相應的值,
    * 之前我們說過,"當前 ApplicationContext 持有一個 BeanFactory",這裡解釋了第一行
    * ApplicationContext 繼承了 ResourceLoader、ApplicationEventPublisher、MessageSource
    * 所以對於這幾個,可以賦值為 this,注意 this 是一個 ApplicationContext
    * 那這裡怎麼沒看到為 MessageSource 賦值呢?那是因為 MessageSource 被註冊成為了一個普通的 bean
    */
   beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   beanFactory.registerResolvableDependency(ResourceLoader.class, this);
   beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
   beanFactory.registerResolvableDependency(ApplicationContext.class, this);

   // 這個 BeanPostProcessor 也很簡單,在 bean 例項化後,如果是 ApplicationListener 的子類,
   // 那麼將其新增到 listener 列表中,可以理解成:註冊事件監聽器
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // Detect a LoadTimeWeaver and prepare for weaving, if found.
   // 這裡涉及到特殊的 bean,名為:loadTimeWeaver,這不是我們的重點,忽略它
   if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      // Set a temporary ClassLoader for type matching.
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }

   /**
    * 從下面幾行程式碼我們可以知道,Spring 往往很 "智慧" 就是因為它會幫我們預設註冊一些有用的 bean,
    * 我們也可以選擇覆蓋
    */

   // 如果沒有定義 "environment" 這個 bean,那麼 Spring 會 "手動" 註冊一個
   if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
   // 如果沒有定義 "systemProperties" 這個 bean,那麼 Spring 會 "手動" 註冊一個
   if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
   // 如果沒有定義 "systemEnvironment" 這個 bean,那麼 Spring 會 "手動" 註冊一個
   if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}

在上面這塊程式碼中,Spring 對一些特殊的 bean 進行了處理,讀者如果暫時還不能消化它們也沒有關係,慢慢往下看。

初始化所有的 singleton beans

我們的重點當然是 finishBeanFactoryInitialization(beanFactory); 這個巨頭了,這裡會負責初始化所有的 singleton beans。

注意,後面的描述中,我都會使用初始化預初始化來代表這個階段。主要是 Spring 需要在這個階段完成所有的 singleton beans 的例項化。

我們來總結一下,到目前為止,應該說 BeanFactory 已經建立完成,並且所有的實現了 BeanFactoryPostProcessor 介面的 Bean 都已經初始化並且其中的 postProcessBeanFactory(factory) 方法已經得到執行了。所有實現了 BeanPostProcessor 介面的 Bean 也都完成了初始化。

剩下的就是初始化其他還沒被初始化的 singleton beans 了,我們知道它們是單例的,如果沒有設定懶載入,那麼 Spring 會在接下來初始化所有的 singleton beans。

// AbstractApplicationContext.java 834

// 初始化剩餘的 singleton beans
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {

   // 首先,初始化名字為 conversionService 的 Bean。本著送佛送到西的精神,我在附錄中簡單介紹了一下 ConversionService,因為這實在太實用了
   // 什麼,看程式碼這裡沒有初始化 Bean 啊!
   // 注意了,初始化的動作包裝在 beanFactory.getBean(...) 中,這裡先不說細節,先往下看吧
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.has