Spring IoC容器源碼閱讀

最近磕Spring源碼有一段時間,直接上手閱讀的難度還是非常大,體系龐大且分支線繁雜,我在閱讀的時候,進入各種實現(xiàn)類很容易繞的不知所蹤了,因此打算把閱讀的Spring源碼做一個總結,把核心脈絡給梳理出來,后面根據(jù)Spring的核心實現(xiàn)也嘗試自己去手寫一個簡易版的Easy-Spring項目,體會下手擼造輪子的感覺。

IoC容器的頂級類:BeanFactory,負責生產(chǎn) bean 的工廠,同時管理各個 bean 實例,可以先來看下和 BeanFactory 接口相關的主要的繼承結構:

BeanFactory體系

除了 BeanFactory 接口外,還有一個重要的 ApplicationContext 其實就是一個 BeanFactory,但是BeanFactory 只提供了最簡單的容器的功能(實例化對象和拿對象的功能),所以被稱為低級容器,而ApplicationContext 則是一個高級的容器,提供了更多的有用的功能,如國際化、訪問資源、消息發(fā)送、響應機制等。下面就從IoC容器的啟動開始講起:

Refresh主流程


首先是IoC的容器啟動,第一步要從 ClassPathXmlApplicationContext 的構造方法說起,通過分析ClassPathXmlApplicationContext類,首先是調用自己的構造方法,然后開始調用最重要的refresh()方法,容器可以去調用 refresh() 這個方法重建ApplicationContext 的,refresh()會將原來的 ApplicationContext 銷毀,然后再重新執(zhí)行一次初始化操作。此處貼上最核心的refresh()方法源碼與解析:

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

      // 準備工作,記錄下容器的啟動時間、標記“已啟動”狀態(tài)、處理配置文件中的占位符
      prepareRefresh();

      // 這步比較關鍵,這步完成后,配置文件就會解析成一個個 Bean 定義,注冊到 BeanFactory 中,
      // 當然,這里說的 Bean 還沒有初始化,只是配置信息都提取出來了,
      // 注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動注冊幾個特殊的 bean
      // 這塊待會會展開說
      prepareBeanFactory(beanFactory);

      try {
         // 【這里需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現(xiàn)了此接口,
         // 那么在容器初始化以后,Spring 會負責調用里面的 postProcessBeanFactory 方法?!?
         // 這里是提供給子類的擴展點,到這里的時候,所有的 Bean 都加載、注冊完成了,但是都還沒有初始化
         // 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現(xiàn)類或做點什么事
         postProcessBeanFactory(beanFactory);
         // 調用 BeanFactoryPostProcessor 各個實現(xiàn)類的 postProcessBeanFactory(factory) 回調方法
         invokeBeanFactoryPostProcessors(beanFactory);          
         // 注冊 BeanPostProcessor 的實現(xiàn)類,注意看和 BeanFactoryPostProcessor 的區(qū)別
         // 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個方法分別在 Bean 初始化之前和初始化之后得到執(zhí)行。這里僅僅是注冊,之后會看到回調這兩方法的時機
         registerBeanPostProcessors(beanFactory);

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

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

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

         // 注冊事件監(jiān)聽器,監(jiān)聽器需要實現(xiàn) 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.
         // 銷毀已經(jīng)初始化的 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();
      }
   }
}

主要去看兩個關鍵方法:

  • ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  • finishBeanFactoryInitialization(beanFactory);

上面的obtainFreshBeanFactory方法是去按照配置文件就會解析成一個個 Bean 定義,注冊到 BeanFactory 中,此處的Bean 還沒有初始化,只是配置信息都提取出來了,保存到了DefaultListableBeanFactory類里面的一個線程安全的HashMap中:

DefaultListableBeanFactory.java,166行

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

而下一個finishBeanFactoryInitialization方法則是去初始化所有的singleton beans,因此這篇文章也主要是圍繞著 refresh 這兩個關鍵方法去梳理工作流程和脈絡。

一、obtainFreshBeanFactory方法


1. 首先,是創(chuàng)建 Bean 容器前的準備工作:

 // 準備工作,記錄下容器的啟動時間、標記“已啟動”狀態(tài)、處理配置文件中的占位符
prepareRefresh();

2. 創(chuàng)建 Bean 容器,加載并注冊 Bean

這里有一個obtainFreshBeanFactory()。注意,這個方法是全文最重要的部分之一,這里將會初始化 BeanFactory、加載 Bean、注冊 Bean等等。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

這個時候找一下 AbstractRefreshableApplicationContext.java 中的refreshBeanFactory()方法,這里要注意的是這一段:

// 加載 Bean 到 BeanFactory 中
loadBeanDefinitions(beanFactory);

BeanFactory 是 Bean 容器,那么 Bean 又是什么呢? BeanDefinition 就是我們所說的 Spring 的 Bean,我們自己定義的各個 Bean 其實會轉換成一個個 BeanDefinition 存在于 Spring 的 BeanFactory 中。Bean 在代碼層面上可以簡單認為是 BeanDefinition 的實例。
BeanDefinition 中保存了我們的 Bean 信息,比如這個 Bean 指向的是哪個類、是否是單例的、是否懶加載、這個 Bean 依賴了哪些 Bean 等等。注意這個里面沒有getInstance()獲取實例的方法

總結一下:到 refresh 中的 obtainFreshBeanFactory 方法,Bean 還沒有初始化,只是配置信息都提取出來了,注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)。

二、finishBeanFactoryInitialization方法


這個 finishBeanFactoryInitialization 方法就是要去初始化所有的 singleton beans,換句話說:Spring 會在這個階段完成所有的 singleton beans 的實例化。首先進入到 AbstractApplicationContext 類里面的 finishBeanFactoryInitialization 方法:

// AbstractApplicationContext.java 834

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // 開始初始化
   beanFactory.preInstantiateSingletons();
}

又要換一個實現(xiàn)類 DefaultListableBeanFactory 才開始做初始化工作,這里要分兩種情況,是否為工廠Bean類型
// DefaultListableBeanFactory 728

public void preInstantiateSingletons() throws BeansException {
            // FactoryBean 的話,在 beanName 前面加上 ‘&’ 符號。再調用 getBean,getBean 方法別急
            final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
            // 對于普通的 Bean,只要調用 getBean(beanName) 這個方法就可以進行初始化了
            getBean(beanName);
}

AbstractBeanFactory類

接下來,我們就進入到非常重要的 AbstractBeanFactory 類里面的 getBean(beanName) 方法了,這個方法我們經(jīng)常用來從 BeanFactory 中獲取一個 Bean,初始化的getBean也在這個方法里封裝。這里的流程大致概括一下就是:

  • 1、先處理Bean 的名稱,因為如果以“&”開頭的Bean名稱表示獲取的是對應的 FactoryBean 對象;
  • 2、從緩存中獲取單例Bean,有則進一步判斷這個Bean是不是在創(chuàng)建中,如果是的就等待創(chuàng)建完畢,否則直接返回這個Bean對象
  • 3、如果不存在單例Bean緩存,則先進行循環(huán)依賴的解析
  • 4、解析完畢之后先獲取父類BeanFactory,獲取到了則調用父類的getBean方法,不存在則先合并然后創(chuàng)建Bean
    // AbstractBeanFactory 196
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
   // 獲取一個 “正統(tǒng)的” beanName,處理兩種情況,一個是前面說的 FactoryBean(前面帶 ‘&’),
   // 一個是別名問題,因為這個方法是 getBean,獲取 Bean 用的,你要是傳一個別名進來,是完全可以的
   final String beanName = transformedBeanName(name);

   // 檢查下是不是已經(jīng)創(chuàng)建過了
   Object sharedInstance = getSingleton(beanName);

   // 檢查一下這個 BeanDefinition 在容器中是否存在
   BeanFactory parentBeanFactory = getParentBeanFactory();

   // 先初始化依賴的所有 Bean,這個很好理解。
   // 注意,這里的依賴指的是 depends-on 中定義的依賴
   String[] dependsOn = mbd.getDependsOn();

   // 如果是 singleton scope 的,創(chuàng)建 singleton 的實例
   if (mbd.isSingleton()) {
      return createBean(beanName, mbd, args);
   }
}

把上面的代碼串起來一張流程圖是長這個樣子的:

AbstractBeanFactory類中獲取Bean的流程

假設這個是第一次去初始化bean那么就會走入到本 AbstractBeanFactory 類里面一個虛函數(shù)中去:

protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException;

AbstractAutowireCapableBeanFactory類

AbstractAutowireCapableBeanFactory 這個類會實現(xiàn)createBean的方法,主要是在真正創(chuàng)建bean之前做一些前置的檢查條件,可以很快過一下,進入到真正的 doCreateBean 方法中去。
// AbstractAutowireCapableBeanFactory 447

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}

接下來我們挑 doCreateBean 中的三個細節(jié)出來說說。流程圖如下:

  • 1、先檢查 instanceWrapper變量是不是null,這里一般是null,除非當前正在創(chuàng)建的Bean在 factoryBeanInstanceCache中存在這個是保存還沒創(chuàng)建完成的FactoryBean的集合。
  • 2、調用createBeanInstance方法實例化Bean,這個方法在后面會講解
  • 3、如果當前 RootBeanDefinition對象還沒有調用過實現(xiàn)了的 MergedBeanDefinitionPostProcessor 接口的方法,則會進行調用 。
  • 4、 當滿足以下三點(1)是單例Bean,(2)嘗試解析bean之間的循環(huán)引用,(3)bean目前正在創(chuàng)建中
    則會進一步檢查是否實現(xiàn)了 SmartInstantiationAwareBeanPostProcessor接口如果實現(xiàn)了則調用是實現(xiàn)的 getEarlyBeanReference方法
  • 5、 調用 populateBean方法進行屬性填充,這里后面會講解
  • 6、 調用 initializeBean方法對Bean進行初始化,這里后面會講解
創(chuàng)建bean的流程圖

把上面的核心步驟抽取出來也就是三個方法:一個是創(chuàng)建 Bean 實例的 createBeanInstance 方法,一個是依賴注入的 populateBean 方法,還有就是回調方法 initializeBean。抽取執(zhí)行的關鍵代碼位置就在如下三處:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {
   if (instanceWrapper == null) {
      // 說明不是 FactoryBean,這里實例化 Bean,這里非常關鍵,細節(jié)之后再說
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   try {
      // 這一步也是非常關鍵的,這一步負責屬性裝配,因為前面的實例只是實例化了,并沒有設值,這里就是設值
      populateBean(beanName, mbd, instanceWrapper);
      if (exposedObject != null) {
         // 還記得 init-method 嗎?還有 InitializingBean 接口?還有 BeanPostProcessor 接口?
         // 這里就是處理 bean 初始化完成后的各種回調
         exposedObject = initializeBean(beanName, exposedObject, mbd);
      }
   } catch (Throwable ex) {}
}
a.創(chuàng)建 Bean 實例

我們先看看 createBeanInstance 方法。需要說明的是,這個方法如果每個分支都分析下去,必然也是極其復雜冗長的,我們挑重點說。此方法的目的就是實例化我們指定的類。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
   // 調用無參構造函數(shù)
   return instantiateBean(beanName, mbd);
}

挑個簡單的無參構造函數(shù)構造實例來看看:

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
   // 實例化,關鍵的地方
   beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}

// SimpleInstantiationStrategy 59

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
   // 如果不存在方法覆寫,那就使用 java 反射進行實例化,否則使用 CGLIB,
   // 方法覆寫 請參見附錄"方法注入"中對 lookup-method 和 replaced-method 的介紹
   if (bd.getMethodOverrides().isEmpty()) {
      // 利用構造方法進行實例化
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // 存在方法覆寫,利用 CGLIB 來完成實例化,需要依賴于 CGLIB 生成子類,這里就不展開了。
      // tips: 因為如果不使用 CGLIB 的話,存在 override 的情況 JDK 并沒有提供相應的實例化支持
      return instantiateWithMethodInjection(bd, beanName, owner);
   }
}
b.bean 屬性注入

看完了 createBeanInstance(...) 方法,我們來看看 populateBean(...) 方法,該方法負責進行屬性設值,處理依賴。
// AbstractAutowireCapableBeanFactory 1203

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
   // bean 實例的所有屬性都在這里了
   PropertyValues pvs = mbd.getPropertyValues();
   // 設置 bean 實例的屬性值
   applyPropertyValues(beanName, mbd, bw, pvs);
c.initializeBean 初始化

屬性注入完成后,這一步其實就是處理各種回調了,這塊代碼比較簡單。

到此就完成了Spring中IoC容器一遍串講,已經(jīng)縮減了大量非關鍵代碼流程,體現(xiàn)出了主脈絡,但是關于創(chuàng)建 bean 這一塊還可以在下一篇文章中重點安排下,那一塊很精華值得深入閱讀,主鏈路的講解就先到這里。

最后講一下源碼閱讀方式,這里有兩種:一種是直接初始化一個Spring的工程,然后下載相應的依賴源碼,順著源碼一點點讀,非常簡便,缺點也很明顯不能在源碼基礎上做筆記+注釋。還有一種是直接從github上fork源碼分支到本地,然后在本地源碼基礎上加上關鍵的注釋,并且在關鍵的分支循環(huán)上都說明一下原因,雖然這樣的源碼閱讀稍微麻煩點,但是讀過以后記憶更深刻,而且可以把注釋筆記一并提交到自己git代碼倉庫里,時刻溫故知新。

主要參考目錄


1.Spring IOC 容器源碼分析
2.Spring 創(chuàng)建Bean流程
3.Spring的Bean生命周期,11 張高清流程圖及代碼,深度解析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容