Spring IOC源碼分析

Spring 最重要的概念是 IOC 和 AOP旗们,本篇文章其實(shí)就是要帶領(lǐng)大家來分析下 Spring 的 IOC 容器球切。既然大家平時(shí)都要用到 Spring缘眶,怎么可以不好好了解 Spring 呢?閱讀本文并不能讓你成為 Spring 專家网梢,不過一定有助于大家理解 Spring 的很多概念碧注,幫助大家排查應(yīng)用中和 Spring 相關(guān)的一些問題嚣伐。

本文采用的源碼版本是 4.3.11.RELEASE,算是 5.0.x 前比較新的版本了萍丐。為了降低難度轩端,本文所說的所有的內(nèi)容都是基于 xml 的配置的方式,實(shí)際使用已經(jīng)很少人這么做了逝变,至少不是純 xml 配置基茵,不過從理解源碼的角度來看用這種方式來說無疑是最合適的刻撒。

閱讀建議:讀者至少需要知道怎么配置 Spring,了解 Spring 中的各種概念耿导,少部分內(nèi)容我還假設(shè)讀者使用過 SpringMVC。本文要說的 IOC 總體來說有兩處地方最重要态贤,一個(gè)是創(chuàng)建 Bean 容器舱呻,一個(gè)是初始化 Bean,如果讀者覺得一次性看完本文壓力有點(diǎn)大悠汽,那么可以按這個(gè)思路分兩次消化箱吕。讀者不一定對 Spring 容器的源碼感興趣,也許附錄部分介紹的知識對讀者有些許作用柿冲。

希望通過本文可以讓讀者不懼怕閱讀 Spring 源碼茬高,也希望大家能反饋表述錯(cuò)誤或不合理的地方。

引言

先看下最基本的啟動(dòng) Spring 容器的例子:

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

以上代碼就可以利用配置文件來啟動(dòng)一個(gè) Spring 容器了假抄,請使用 maven 的小伙伴直接在 dependencies 中加上以下依賴即可怎栽,個(gè)人比較反對那些不知道要添加什么依賴,然后把 Spring 的所有相關(guān)的東西都加進(jìn)來的方式宿饱。

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

spring-context 會自動(dòng)將 spring-core熏瞄、spring-beans、spring-aop谬以、spring-expression 這幾個(gè)基礎(chǔ) jar 包帶進(jìn)來强饮。

多說一句,很多開發(fā)者入門就直接接觸的 SpringMVC为黎,對 Spring 其實(shí)不是很了解邮丰,Spring 是漸進(jìn)式的工具,并不具有很強(qiáng)的侵入性铭乾,它的模塊也劃分得很合理剪廉,即使你的應(yīng)用不是 web 應(yīng)用,或者之前完全沒有使用到 Spring片橡,而你就想用 Spring 的依賴注入這個(gè)功能妈经,其實(shí)完全是可以的,它的引入不會對其他的組件產(chǎn)生沖突捧书。

廢話說完吹泡,我們繼續(xù)。ApplicationContext context = new ClassPathXmlApplicationContext(...) 其實(shí)很好理解经瓷,從名字上就可以猜出一二爆哑,就是在 ClassPath 中尋找 xml 配置文件,根據(jù) xml 文件內(nèi)容來構(gòu)建 ApplicationContext舆吮。當(dāng)然揭朝,除了 ClassPathXmlApplicationContext 以外队贱,我們也還有其他構(gòu)建 ApplicationContext 的方案可供選擇,我們先來看看大體的繼承結(jié)構(gòu)是怎么樣的:

image

讀者可以大致看一下類名潭袱,源碼分析的時(shí)候不至于找不著看哪個(gè)類柱嫌,因?yàn)?Spring 為了適應(yīng)各種使用場景,提供的各個(gè)接口都可能有很多的實(shí)現(xiàn)類屯换。對于我們來說编丘,就是揪著一個(gè)完整的分支看完。
當(dāng)然彤悔,讀本文的時(shí)候讀者也不必太擔(dān)心嘉抓,每個(gè)代碼塊分析的時(shí)候,我都會告訴讀者我們在說哪個(gè)類第幾行晕窑。

我們可以看到抑片,ClassPathXmlApplicationContext 兜兜轉(zhuǎn)轉(zhuǎn)了好久才到 ApplicationContext 接口,同樣的杨赤,我們也可以使用綠顏色的 FileSystemXmlApplicationContextAnnotationConfigApplicationContext 這兩個(gè)類敞斋。

FileSystemXmlApplicationContext 的構(gòu)造函數(shù)需要一個(gè) xml 配置文件在系統(tǒng)中的路徑,其他和 ClassPathXmlApplicationContext 基本上一樣望拖。

AnnotationConfigApplicationContext 是基于注解來使用的渺尘,它不需要配置文件,采用 java 配置類和各種注解來配置说敏,是比較簡單的方式鸥跟,也是大勢所趨吧。

不過本文旨在幫助大家理解整個(gè)構(gòu)建流程盔沫,所以決定使用 ClassPathXmlApplicationContext 進(jìn)行分析医咨。

我們先來一個(gè)簡單的例子來看看怎么實(shí)例化 ApplicationContext。

首先架诞,定義一個(gè)接口:

public interface MessageService {
    String getMessage();
}

定義接口實(shí)現(xiàn)類:

public class MessageServiceImpl implements MessageService {

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

接下來拟淮,我們在 resources 目錄新建一個(gè)配置文件,文件名隨意谴忧,通常叫 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) {
        // 用我們的配置文件來啟動(dòng)一個(gè) ApplicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");

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

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

以上例子很簡單沾谓,不過也夠引出本文的主題了委造,就是怎么樣通過配置文件來啟動(dòng) Spring 的 ApplicationContext?也就是我們今天要分析的 IOC 的核心了均驶。ApplicationContext 啟動(dòng)過程中昏兆,會負(fù)責(zé)創(chuàng)建實(shí)例 Bean,往各個(gè) Bean 中注入依賴等妇穴。

BeanFactory 簡介

BeanFactory爬虱,從名字上也很好理解隶债,生產(chǎn) bean 的工廠,它負(fù)責(zé)生產(chǎn)和管理各個(gè) bean 實(shí)例跑筝。

初學(xué)者可別以為我之前說那么多和 BeanFactory 無關(guān)死讹,前面說的 ApplicationContext 其實(shí)就是一個(gè) BeanFactory。我們來看下和 BeanFactory 接口相關(guān)的主要的繼承結(jié)構(gòu):

image

我想曲梗,大家看完這個(gè)圖以后回俐,可能就不是很開心了。ApplicationContext 往下的繼承結(jié)構(gòu)前面一張圖說過了稀并,這里就不重復(fù)了。這張圖呢单默,背下來肯定是不需要的碘举,有幾個(gè)重點(diǎn)和大家說明下就好。

  1. ApplicationContext 繼承了 ListableBeanFactory搁廓,這個(gè) Listable 的意思就是引颈,通過這個(gè)接口,我們可以獲取多個(gè) Bean境蜕,大家看源碼會發(fā)現(xiàn)蝙场,最頂層 BeanFactory 接口的方法都是獲取單個(gè) Bean 的。
  2. ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞本身已經(jīng)能說明問題了,也就是說我們可以在應(yīng)用中起多個(gè) BeanFactory蒂窒,然后可以將各個(gè) BeanFactory 設(shè)置為父子關(guān)系蔚叨。
  3. AutowireCapableBeanFactory 這個(gè)名字中的 Autowire 大家都非常熟悉,它就是用來自動(dòng)裝配 Bean 用的飞蹂,但是仔細(xì)看上圖,ApplicationContext 并沒有繼承它,不過不用擔(dān)心弊知,不使用繼承,不代表不可以使用組合粱快,如果你看到 ApplicationContext 接口定義中的最后一個(gè)方法 getAutowireCapableBeanFactory() 就知道了秩彤。
  4. ConfigurableListableBeanFactory 也是一個(gè)特殊的接口,看圖事哭,特殊之處在于它繼承了第二層所有的三個(gè)接口漫雷,而 ApplicationContext 沒有。這點(diǎn)之后會用到慷蠕。
  5. 請先不用花時(shí)間在其他的接口和類上珊拼,先理解我說的這幾點(diǎn)就可以了。

然后流炕,請讀者打開編輯器澎现,翻一下 BeanFactory仅胞、ListableBeanFactory、HierarchicalBeanFactory剑辫、AutowireCapableBeanFactory干旧、ApplicationContext 這幾個(gè)接口的代碼,大概看一下各個(gè)接口中的方法妹蔽,大家心里要有底椎眯,限于篇幅,我就不貼代碼介紹了胳岂。

啟動(dòng)過程分析

下面將會是冗長的代碼分析编整,記住,一定要自己打開源碼來看乳丰,不然純看是很累的掌测。

第一步,我們肯定要從 ClassPathXmlApplicationContext 的構(gòu)造方法說起产园。

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

  // 如果已經(jīng)有 ApplicationContext 并需要配置成父子關(guān)系汞斧,那么調(diào)用這個(gè)構(gòu)造方法
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  ...
  public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
      throws BeansException {

    super(parent);
    // 根據(jù)提供的路徑,處理成配置文件數(shù)組(以分號什燕、逗號粘勒、空格、tab屎即、換行符分割)
    setConfigLocations(configLocations);
    if (refresh) {
      refresh(); // 核心方法
    }
  }
    ...
}

接下來庙睡,就是 refresh(),這里簡單說下為什么是 refresh()技俐,而不是 init() 這種名字的方法埃撵。因?yàn)?ApplicationContext 建立起來以后,其實(shí)我們是可以通過調(diào)用 refresh() 這個(gè)方法重建的虽另,refresh() 會將原來的 ApplicationContext 銷毀暂刘,然后再重新執(zhí)行一次初始化操作。

往下看捂刺,refresh() 方法里面調(diào)用了那么多方法谣拣,就知道肯定不簡單了,請讀者先看個(gè)大概族展,細(xì)節(jié)之后會詳細(xì)說森缠。

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

      // 準(zhǔn)備工作,記錄下容器的啟動(dòng)時(shí)間、標(biāo)記“已啟動(dòng)”狀態(tài)宾茂、處理配置文件中的占位符
      prepareRefresh();

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

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

      try {
         // 【這里需要知道 BeanFactoryPostProcessor 這個(gè)知識點(diǎn)蒋伦,Bean 如果實(shí)現(xiàn)了此接口,
         // 那么在容器初始化以后焚鹊,Spring 會負(fù)責(zé)調(diào)用里面的 postProcessBeanFactory 方法凉敲。】

         // 這里是提供給子類的擴(kuò)展點(diǎn)寺旺,到這里的時(shí)候,所有的 Bean 都加載势决、注冊完成了阻塑,但是都還沒有初始化
         // 具體的子類可以在這步的時(shí)候添加一些特殊的 BeanFactoryPostProcessor 的實(shí)現(xiàn)類或做點(diǎn)什么事
         postProcessBeanFactory(beanFactory);
         // 調(diào)用 BeanFactoryPostProcessor 各個(gè)實(shí)現(xiàn)類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注冊 BeanPostProcessor 的實(shí)現(xiàn)類,注意看和 BeanFactoryPostProcessor 的區(qū)別
         // 此接口兩個(gè)方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個(gè)方法分別在 Bean 初始化之前和初始化之后得到執(zhí)行果复。注意陈莽,到這里 Bean 還沒初始化
         registerBeanPostProcessors(beanFactory);

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

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

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

         // 注冊事件監(jiān)聽器私植,監(jiān)聽器需要實(shí)現(xiàn) ApplicationListener 接口。這也不是我們的重點(diǎn)车酣,過
         registerListeners();

         // 重點(diǎn)曲稼,重點(diǎn),重點(diǎn)
         // 初始化所有的 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();
      }
   }
}

下面娘摔,我們開始一步步來肢解這個(gè) refresh() 方法窄坦。

創(chuàng)建 Bean 容器前的準(zhǔn)備工作

這個(gè)比較簡單,直接看代碼中的幾個(gè)注釋即可。

protected void prepareRefresh() {
   // 記錄啟動(dòng)時(shí)間鸭津,
   // 將 active 屬性設(shè)置為 true彤侍,closed 屬性設(shè)置為 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();

   // 校驗(yàn) xml 配置文件
   getEnvironment().validateRequiredProperties();

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

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

我們回到 refresh() 方法中的下一行 obtainFreshBeanFactory()拥刻。

注意,這個(gè)方法是全文最重要的部分之一父泳,這里將會初始化 BeanFactory般哼、加載 Bean、注冊 Bean 等等惠窄。

當(dāng)然蒸眠,這步結(jié)束后,Bean 并沒有完成初始化杆融。這里指的是 Bean 實(shí)例并未在這一步生成楞卡。

// AbstractApplicationContext.java

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // 關(guān)閉舊的 BeanFactory (如果有),創(chuàng)建新的 BeanFactory脾歇,加載 Bean 定義蒋腮、注冊 Bean 等等
   refreshBeanFactory();

   // 返回剛剛創(chuàng)建的 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 中已經(jīng)加載過 BeanFactory 了,銷毀所有 Bean藕各,關(guān)閉 BeanFactory
   // 注意池摧,應(yīng)用中 BeanFactory 本來就是可以多個(gè)的,這里可不是說應(yīng)用全局是否有 BeanFactory激况,而是當(dāng)前
   // ApplicationContext 是否有 BeanFactory
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // 初始化一個(gè) DefaultListableBeanFactory作彤,為什么用這個(gè),我們馬上說乌逐。
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // 用于 BeanFactory 的序列化竭讳,我想不部分人應(yīng)該都用不到
      beanFactory.setSerializationId(getId());

      // 下面這兩個(gè)方法很重要,別跟丟了浙踢,具體細(xì)節(jié)之后說
      // 設(shè)置 BeanFactory 的兩個(gè)配置屬性:是否允許 Bean 覆蓋绢慢、是否允許循環(huán)引用
      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);
   }
}

看到這里的時(shí)候,我覺得讀者就應(yīng)該站在高處看 ApplicationContext 了洛波,ApplicationContext 繼承自 BeanFactory呐芥,但是它不應(yīng)該被理解為 BeanFactory 的實(shí)現(xiàn)類,而是說其內(nèi)部持有一個(gè)實(shí)例化的 BeanFactory(DefaultListableBeanFactory)奋岁。以后所有的 BeanFactory 相關(guān)的操作其實(shí)是委托給這個(gè)實(shí)例來處理的思瘟。

我們說說為什么選擇實(shí)例化 DefaultListableBeanFactory ?前面我們說了有個(gè)很重要的接口 ConfigurableListableBeanFactory闻伶,它實(shí)現(xiàn)了 BeanFactory 下面一層的所有三個(gè)接口滨攻,我把之前的繼承圖再拿過來大家再仔細(xì)看一下:

image

我們可以看到 ConfigurableListableBeanFactory 只有一個(gè)實(shí)現(xiàn)類 DefaultListableBeanFactory,而且實(shí)現(xiàn)類 DefaultListableBeanFactory 還通過實(shí)現(xiàn)右邊的 AbstractAutowireCapableBeanFactory 通吃了右路。所以結(jié)論就是光绕,最底下這個(gè)家伙 DefaultListableBeanFactory 基本上是最牛的 BeanFactory 了女嘲,這也是為什么這邊會使用這個(gè)類來實(shí)例化的原因。

如果你想要在程序運(yùn)行的時(shí)候動(dòng)態(tài)往 Spring IOC 容器注冊新的 bean诞帐,就會使用到這個(gè)類欣尼。那我們怎么在運(yùn)行時(shí)獲得這個(gè)實(shí)例呢?
之前我們說過 ApplicationContext 接口能獲取到 AutowireCapableBeanFactory停蕉,就是最右上角那個(gè)愕鼓,然后它向下轉(zhuǎn)型就能得到 DefaultListableBeanFactory 了。

在繼續(xù)往下之前慧起,我們需要先了解 BeanDefinition菇晃。我們說 BeanFactory 是 Bean 容器,那么 Bean 又是什么呢蚓挤?

這里的 BeanDefinition 就是我們所說的 Spring 的 Bean磺送,我們自己定義的各個(gè) Bean 其實(shí)會轉(zhuǎn)換成一個(gè)個(gè) BeanDefinition 存在于 Spring 的 BeanFactory 中。

所以灿意,如果有人問你 Bean 是什么的時(shí)候估灿,你要知道 Bean 在代碼層面上可以認(rèn)為是 BeanDefinition 的實(shí)例。

BeanDefinition 中保存了我們的 Bean 信息缤剧,比如這個(gè) Bean 指向的是哪個(gè)類馅袁、是否是單例的、是否懶加載鞭执、這個(gè) Bean 依賴了哪些 Bean 等等。

BeanDefinition 接口定義

我們來看下 BeanDefinition 的接口定義:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

   // 我們可以看到芒粹,默認(rèn)只提供 sington 和 prototype 兩種兄纺,
   // 很多讀者可能知道還有 request, session, globalSession, application, websocket 這幾種,
   // 不過化漆,它們屬于基于 web 的擴(kuò)展估脆。
   String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
   String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

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

   // 設(shè)置父 Bean座云,這里涉及到 bean 繼承疙赠,不是 java 繼承。請參見附錄的詳細(xì)介紹
   // 一句話就是:繼承父 Bean 的配置信息而已
   void setParentName(String parentName);

   // 獲取父 Bean
   String getParentName();

   // 設(shè)置 Bean 的類名稱朦拖,將來是要通過反射來生成實(shí)例的
   void setBeanClassName(String beanClassName);

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

   // 設(shè)置 bean 的 scope
   void setScope(String scope);

   String getScope();

   // 設(shè)置是否懶加載
   void setLazyInit(boolean lazyInit);

   boolean isLazyInit();

   // 設(shè)置該 Bean 依賴的所有的 Bean圃阳,注意,這里的依賴不是指屬性依賴(如 @Autowire 標(biāo)記的)璧帝,
   // 是 depends-on="" 屬性設(shè)置的值捍岳。
   void setDependsOn(String... dependsOn);

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

   // 設(shè)置該 Bean 是否可以注入到其他 Bean 中,只對根據(jù)類型注入有效,
   // 如果根據(jù)名稱注入锣夹,即使這邊設(shè)置了 false页徐,也是可以的
   void setAutowireCandidate(boolean autowireCandidate);

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

   // 主要的。同一接口的多個(gè)實(shí)現(xiàn)银萍,如果不指定名字的話变勇,Spring 會優(yōu)先選擇設(shè)置 primary 為 true 的 bean
   void setPrimary(boolean primary);

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

   // 如果該 Bean 采用工廠方法生成,指定工廠名稱贴唇。對工廠不熟悉的讀者搀绣,請參加附錄
   // 一句話就是:有些實(shí)例不是用反射生成的,而是用工廠模式生成的
   void setFactoryBeanName(String factoryBeanName);
   // 獲取工廠名稱
   String getFactoryBeanName();
   // 指定工廠類中的 工廠方法名稱
   void setFactoryMethodName(String factoryMethodName);
   // 獲取工廠類中的 工廠方法名稱
   String getFactoryMethodName();

   // 構(gòu)造器參數(shù)
   ConstructorArgumentValues getConstructorArgumentValues();

   // Bean 中的屬性值滤蝠,后面給 bean 注入屬性值的時(shí)候會說到
   MutablePropertyValues getPropertyValues();

   // 是否 singleton
   boolean isSingleton();

   // 是否 prototype
   boolean isPrototype();

   // 如果這個(gè) Bean 是被設(shè)置為 abstract豌熄,那么不能實(shí)例化,
   // 常用于作為 父bean 用于繼承物咳,其實(shí)也很少用......
   boolean isAbstract();

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

這個(gè) BeanDefinition 其實(shí)已經(jīng)包含很多的信息了锣险,暫時(shí)不清楚所有的方法對應(yīng)什么東西沒關(guān)系,希望看完本文后讀者可以徹底搞清楚里面的所有東西览闰。
這里接口雖然那么多芯肤,但是沒有類似 getInstance() 這種方法來獲取我們定義的類的實(shí)例,真正的我們定義的類生成的實(shí)例到哪里去了呢压鉴?別著急崖咨,這個(gè)要很后面才能講到。

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

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);

雖然只有兩個(gè)方法击蹲,但路還很長啊。婉宰。歌豺。

customizeBeanFactory

customizeBeanFactory(beanFactory) 比較簡單,就是配置是否允許 BeanDefinition 覆蓋心包、是否允許循環(huán)引用类咧。

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

BeanDefinition 的覆蓋問題可能會有開發(fā)者碰到這個(gè)坑,就是在配置文件中定義 bean 時(shí)使用了相同的 id 或 name蟹腾,默認(rèn)情況下痕惋,allowBeanDefinitionOverriding 屬性為 null,如果在同一配置文件中重復(fù)了娃殖,會拋錯(cuò)值戳,但是如果不是同一配置文件中,會發(fā)生覆蓋炉爆。

循環(huán)引用也很好理解:A 依賴 B述寡,而 B 依賴 A柿隙。或 A 依賴 B鲫凶,B 依賴 C禀崖,而 C 依賴 A。

默認(rèn)情況下螟炫,Spring 允許循環(huán)依賴波附,當(dāng)然如果你在 A 的構(gòu)造方法中依賴 B,在 B 的構(gòu)造方法中依賴 A 是不行的昼钻。

至于這兩個(gè)屬性怎么配置掸屡?我在附錄中進(jìn)行了介紹,尤其對于覆蓋問題然评,很多人都希望禁止出現(xiàn) Bean 覆蓋仅财,可是 Spring 默認(rèn)是不同文件的時(shí)候可以覆蓋的。

之后的源碼中還會出現(xiàn)這兩個(gè)屬性碗淌,讀者有個(gè)印象就可以了盏求。

加載 Bean: loadBeanDefinitions

接下來是最重要的 loadBeanDefinitions(beanFactory) 方法了,這個(gè)方法將根據(jù)配置亿眠,加載各個(gè) Bean碎罚,然后放到 BeanFactory 中。

讀取配置的操作在 XmlBeanDefinitionReader 中纳像,其負(fù)責(zé)加載配置荆烈、解析。

// AbstractXmlApplicationContext.java 80

/** 我們可以看到竟趾,此方法將通過一個(gè) XmlBeanDefinitionReader 實(shí)例來加載各個(gè) Bean憔购。*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 給這個(gè) BeanFactory 實(shí)例化一個(gè) 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,其實(shí)這個(gè)是提供給子類覆寫的岔帽,
   // 我看了一下玫鸟,沒有類覆寫這個(gè)方法,我們姑且當(dāng)做不重要吧
   initBeanDefinitionReader(beanDefinitionReader);
   // 重點(diǎn)來了山卦,繼續(xù)往下
   loadBeanDefinitions(beanDefinitionReader);
}

現(xiàn)在還在這個(gè)類中鞋邑,接下來用剛剛初始化的 Reader 開始來加載 xml 配置诵次,這塊代碼讀者可以選擇性跳過账蓉,不是很重要。也就是說逾一,下面這個(gè)代碼塊铸本,讀者可以很輕松地略過。

// 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) {
      // 2
      reader.loadBeanDefinitions(configLocations);
   }
}

// 上面雖然有兩個(gè)分支遵堵,不過第二個(gè)分支很快通過解析路徑轉(zhuǎn)換為 Resource 以后也會進(jìn)到這里
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   // 注意這里是個(gè) for 循環(huán)箱玷,也就是每個(gè)文件是一個(gè) resource
   for (Resource resource : resources) {
      // 繼續(xù)往下看
      counter += loadBeanDefinitions(resource);
   }
   // 最后返回 counter怨规,表示總共加載了多少的 BeanDefinition
   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());
   }
   // 用一個(gè) 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();
      }
   }
}

// 還在這個(gè)文件中锡足,第 388 行
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // 這里就不看了波丰,將 xml 文件轉(zhuǎn)換為 Document 對象
      Document doc = doLoadDocument(inputSource, resource);
      // 繼續(xù)
      return registerBeanDefinitions(doc, resource);
   }
   catch (...
}
// 還在這個(gè)文件中,第 505 行
// 返回值:返回從當(dāng)前配置文件加載了多少數(shù)量的 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();
   // 從 xml 根節(jié)點(diǎn)開始解析文件
   doRegisterBeanDefinitions(root);
}         

經(jīng)過漫長的鏈路舶得,一個(gè)配置文件終于轉(zhuǎn)換為一顆 DOM 樹了掰烟,注意,這里指的是其中一個(gè)配置文件沐批,不是所有的纫骑,讀者可以看到上面有個(gè) for 循環(huán)的。下面開始從根節(jié)點(diǎn)開始解析:

doRegisterBeanDefinitions:

// DefaultBeanDefinitionDocumentReader 116
protected void doRegisterBeanDefinitions(Element root) {
   // 我們看名字就知道九孩,BeanDefinitionParserDelegate 必定是一個(gè)重要的類先馆,它負(fù)責(zé)解析 Bean 定義,
   // 這里為什么要定義一個(gè) parent? 看到后面就知道了躺彬,是遞歸問題煤墙,
   // 因?yàn)?<beans /> 內(nèi)部是可以定義 <beans /> 的,所以這個(gè)方法的 root 其實(shí)不一定就是 xml 的根節(jié)點(diǎn)顾患,也可以是嵌套在里面的 <beans /> 節(jié)點(diǎn)番捂,從源碼分析的角度,我們當(dāng)做根節(jié)點(diǎn)就好了
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);

   if (this.delegate.isDefaultNamespace(root)) {
      // 這塊說的是根節(jié)點(diǎn) <beans ... profile="dev" /> 中的 profile 是否是當(dāng)前環(huán)境需要的江解,
      // 如果當(dāng)前環(huán)境配置的 profile 不包含此 profile设预,那就直接 return 了,不對此 <beans /> 解析
      // 不熟悉 profile 為何物犁河,不熟悉怎么配置 profile 讀者的請移步附錄區(qū)
      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) 是給子類用的鉤子方法鳖枕,鑒于沒有被使用到,也不是我們的重點(diǎn)桨螺,我們直接跳過宾符。

這里涉及到了 profile 的問題,對于不了解的讀者灭翔,我在附錄中對 profile 做了簡單的解釋魏烫,讀者可以參考一下。

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

// default namespace 涉及到的就四個(gè)標(biāo)簽 <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)) {
               // 解析 default namespace 下面的幾個(gè)元素
               parseDefaultElement(ele, delegate);
            }
            else {
               // 解析其他 namespace 的元素
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

從上面的代碼呐赡,我們可以看到,對于每個(gè)配置來說骏融,分別進(jìn)入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 這兩個(gè)分支了链嘀。

parseDefaultElement(ele, delegate) 代表解析的節(jié)點(diǎn)是 <import />萌狂、<alias /><bean />怀泊、<beans /> 這幾個(gè)茫藏。

這里的四個(gè)標(biāo)簽之所以是 default 的,是因?yàn)樗鼈兪翘幱谶@個(gè) namespace 下定義的:
http://www.springframework.org/schema/beans

又到初學(xué)者科普時(shí)間霹琼,不熟悉 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">

而對于其他的標(biāo)簽碧囊,將進(jìn)入到 delegate.parseCustomElement(element) 這個(gè)分支树灶。如我們經(jīng)常會使用到的 <mvc /><task />糯而、<context />天通、<aop />等。
這些屬于擴(kuò)展熄驼,如果需要使用上面這些 ”非 default“ 標(biāo)簽像寒,那么上面的 xml 頭部的地方也要引入相應(yīng)的 namespace 和 .xsd 文件的路徑,如下所示瓜贾。同時(shí)代碼中需要提供相應(yīng)的 parser 來解析诺祸,如 MvcNamespaceHandler、TaskNamespaceHandler祭芦、ContextNamespaceHandler筷笨、AopNamespaceHandler 等。
假如讀者想分析 <context:property-placeholder location="classpath:xx.properties" /> 的實(shí)現(xiàn)原理龟劲,就應(yīng)該到 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 標(biāo)簽的方法:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      // 處理 <import /> 標(biāo)簽
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      // 處理 <alias /> 標(biāo)簽定義
      // <alias name="fromName" alias="toName"/>
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      // 處理 <bean /> 標(biāo)簽定義昌跌,這也算是我們的重點(diǎn)吧
      processBeanDefinition(ele, delegate);
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // 如果碰到的是嵌套的 <beans /> 標(biāo)簽仰禀,需要遞歸
      doRegisterBeanDefinitions(ele);
   }
}

如果每個(gè)標(biāo)簽都說,那我不吐血蚕愤,你們都要吐血了答恶。我們挑我們的重點(diǎn) <bean /> 標(biāo)簽出來說。

processBeanDefinition 解析 bean 標(biāo)簽

下面是 processBeanDefinition 解析 <bean /> 標(biāo)簽:

// DefaultBeanDefinitionDocumentReader 298

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 將 <bean /> 節(jié)點(diǎn)中的信息提取出來萍诱,然后封裝到一個(gè) BeanDefinitionHolder 中悬嗓,細(xì)節(jié)往下看
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

   // 下面的幾行先不要看,跳過先砂沛,跳過先烫扼,跳過先曙求,后面會繼續(xù)說的

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

繼續(xù)往下看怎么解析之前碍庵,我們先看下 <bean /> 標(biāo)簽中可以定義哪些屬性:

Propertyclass類的全限定名name可指定 id映企、name(用逗號、分號静浴、空格分隔)scope作用域constructor arguments指定構(gòu)造參數(shù)properties設(shè)置屬性的值autowiring modeno(默認(rèn)值)堰氓、byName、byType苹享、 constructorlazy-initialization mode是否懶加載(如果被非懶加載的bean依賴了那么其實(shí)也就不能懶加載了)initialization methodbean 屬性設(shè)置完成后双絮,會調(diào)用這個(gè)方法destruction methodbean 銷毀后的回調(diào)方法

上面表格中的內(nèi)容我想大家都非常熟悉吧,如果不熟悉得问,那就是你不夠了解 Spring 的配置了囤攀。

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

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

    <!-- 可以用下面三種形式指定構(gòu)造參數(shù) -->
    <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>

當(dāng)然,除了上面舉例出來的這些宫纬,還有 factory-bean焚挠、factory-method、<lockup-method />漓骚、<replaced-method />蝌衔、<meta /><qualifier /> 這幾個(gè)蝌蹂,大家是不是熟悉呢噩斟?自己檢驗(yàn)一下自己對 Spring 中 bean 的了解程度。

有了以上這些知識以后孤个,我們再繼續(xù)往里看怎么解析 bean 元素剃允,是怎么轉(zhuǎn)換到 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 屬性的定義按照 “逗號齐鲤、分號硅急、空格” 切分,形成一個(gè) 別名列表數(shù)組佳遂,
   // 當(dāng)然营袜,如果你不定義 name 屬性的話,就是空的了
   // 我在附錄中簡單介紹了一下 id 和 name 的配置丑罪,大家可以看一眼荚板,有個(gè)20秒就可以了
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   String beanName = id;
   // 如果沒有指定id, 那么用別名列表的第一個(gè)名字作為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);
   }

   // 根據(jù) <bean ...>...</bean> 中的配置創(chuàng)建 BeanDefinition,然后把配置中的信息都設(shè)置到實(shí)例中,
   // 細(xì)節(jié)后面細(xì)說吩屹,先知道下面這行結(jié)束后跪另,一個(gè) BeanDefinition 實(shí)例就出來了。
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

   // 到這里煤搜,整個(gè) <bean /> 標(biāo)簽就算解析結(jié)束了免绿,一個(gè) BeanDefinition 就形成了。
   if (beanDefinition != null) {
      // 如果都沒有設(shè)置 id 和 name擦盾,那么此時(shí)的 beanName 就會為 null嘲驾,進(jìn)入下面這塊代碼產(chǎn)生
      // 如果讀者不感興趣的話淌哟,我覺得不需要關(guān)心這塊代碼,對本文源碼分析來說,這些東西不重要
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {// 按照我們的思路,這里 containingBean 是 null 的
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               // 如果我們不定義 id 和 name球恤,那么我們引言里的那個(gè)例子:
               //   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 設(shè)置為 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;
}

然后,我們再看看怎么根據(jù)配置創(chuàng)建 BeanDefinition 實(shí)例的:

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);
      }
      // 創(chuàng)建 BeanDefinition掉弛,然后設(shè)置類信息而已,很簡單喂走,就不貼代碼了
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);

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

      /**
       * 下面的一堆是解析 <bean>......</bean> 內(nèi)部的子元素,
       * 解析出來以后的信息都放到 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;
}

到這里芋肠,我們已經(jīng)完成了根據(jù) <bean /> 配置創(chuàng)建了一個(gè) BeanDefinitionHolder 實(shí)例壁晒。注意,是一個(gè)业栅。

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

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 將 <bean /> 節(jié)點(diǎn)轉(zhuǎn)換為 BeanDefinitionHolder秒咐,就是上面說的一堆
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      // 如果有自定義屬性的話,進(jìn)行相應(yīng)的解析碘裕,先忽略
      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);
      }
      // 注冊完成后携取,發(fā)送事件,本文不展開說這個(gè)
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

大家再仔細(xì)看一下這塊吧帮孔,我們后面就不回來說這個(gè)了雷滋。這里已經(jīng)根據(jù)一個(gè) <bean /> 標(biāo)簽產(chǎn)生了一個(gè) BeanDefinitionHolder 的實(shí)例,這個(gè)實(shí)例里面也就是一個(gè) BeanDefinition 的實(shí)例和它的 beanName文兢、aliases 這三個(gè)信息晤斩,注意,我們的關(guān)注點(diǎn)始終在 BeanDefinition 上:

public class BeanDefinitionHolder implements BeanMetadataElement {

  private final BeanDefinition beanDefinition;

  private final String beanName;

  private final String[] aliases;
...

然后我們準(zhǔn)備注冊這個(gè) BeanDefinition姆坚,最后澳泵,把這個(gè)注冊事件發(fā)送出去。

下面兼呵,我們開始說說注冊 Bean 吧兔辅。

注冊 Bean

// BeanDefinitionReaderUtils 143

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

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

   // 如果還有別名的話,也要根據(jù)別名全部注冊一遍击喂,不然根據(jù)別名就會找不到 Bean 了
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         // alias -> beanName 保存它們的別名信息维苔,這個(gè)很簡單,用一個(gè) map 保存一下就可以了懂昂,
         // 獲取的時(shí)候介时,會先將 alias 轉(zhuǎn)換為 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 覆蓋” 這個(gè)配置嗎?allowBeanDefinitionOverriding
   BeanDefinition oldBeanDefinition;

   // 之后會看到勉失,所有的 Bean 注冊后會放入這個(gè) beanDefinitionMap 中
   oldBeanDefinition = this.beanDefinitionMap.get(beanName);

   // 處理重復(fù)名稱的 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 {
      // 判斷是否已經(jīng)有其他的 Bean 開始初始化了.
      // 注意乱凿,"注冊Bean" 這個(gè)動(dòng)作結(jié)束,Bean 依然還沒有初始化咽弦,我們后面會有大篇幅說初始化過程徒蟆,
      // 在 Spring 容器啟動(dòng)的最后,會 預(yù)初始化 所有的 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 {
         // 最正常的應(yīng)該是進(jìn)到這個(gè)分支型型。

         // 將 BeanDefinition 放到這個(gè) map 中段审,這個(gè) map 保存了所有的 BeanDefinition
         this.beanDefinitionMap.put(beanName, beanDefinition);
         // 這是個(gè) ArrayList,所以會按照 bean 配置的順序保存每一個(gè)注冊的 Bean 的名字
         this.beanDefinitionNames.add(beanName);
         // 這是個(gè) LinkedHashSet闹蒜,代表的是手動(dòng)注冊的 singleton bean寺枉,
         // 注意這里是 remove 方法,到這里的 Bean 當(dāng)然不是手動(dòng)注冊的
         // 手動(dòng)指的是通過調(diào)用以下方法注冊的 bean :
         //     registerSingleton(String beanName, Object singletonObject)
         // 這不是重點(diǎn)绷落,解釋只是為了不讓大家疑惑姥闪。Spring 會在后面"手動(dòng)"注冊一些 Bean,
         // 如 "environment"砌烁、"systemProperties" 等 bean筐喳,我們自己也可以在運(yùn)行時(shí)注冊 Bean 到容器中的
         this.manualSingletonNames.remove(beanName);
      }
      // 這個(gè)不重要,在預(yù)初始化的時(shí)候會用到函喉,不必管它避归。
      this.frozenBeanDefinitionNames = null;
   }

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

總結(jié)一下,到這里已經(jīng)初始化了 Bean 容器管呵,<bean /> 配置也相應(yīng)的轉(zhuǎn)換為了一個(gè)個(gè) BeanDefinition梳毙,然后注冊了各個(gè) BeanDefinition 到注冊中心,并且發(fā)送了注冊事件捐下。

到這里是一個(gè)分水嶺顿天,前面的內(nèi)容都還算比較簡單,大家要清楚地知道前面都做了哪些事情蔑担。

Bean 容器實(shí)例化完成后

說到這里牌废,我們回到 refresh() 方法,我重新貼了一遍代碼啤握,看看我們說到哪了鸟缕。是的,我們才說完 obtainFreshBeanFactory() 方法。

考慮到篇幅懂从,這里開始大幅縮減掉沒必要詳細(xì)介紹的部分授段,大家直接看下面的代碼中的注釋就好了。

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

      // 準(zhǔn)備工作缘薛,記錄下容器的啟動(dòng)時(shí)間窍育、標(biāo)記“已啟動(dòng)”狀態(tài)、處理配置文件中的占位符
      prepareRefresh();

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

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

      try {
         // 【這里需要知道 BeanFactoryPostProcessor 這個(gè)知識點(diǎn)士骤,Bean 如果實(shí)現(xiàn)了此接口溶其,
         // 那么在容器初始化以后,Spring 會負(fù)責(zé)調(diào)用里面的 postProcessBeanFactory 方法敦间∑刻樱】

         // 這里是提供給子類的擴(kuò)展點(diǎn),到這里的時(shí)候廓块,所有的 Bean 都加載厢绝、注冊完成了,但是都還沒有初始化
         // 具體的子類可以在這步的時(shí)候添加一些特殊的 BeanFactoryPostProcessor 的實(shí)現(xiàn)類或做點(diǎn)什么事
         postProcessBeanFactory(beanFactory);
         // 調(diào)用 BeanFactoryPostProcessor 各個(gè)實(shí)現(xiàn)類的 postProcessBeanFactory(factory) 回調(diào)方法
         invokeBeanFactoryPostProcessors(beanFactory);          

         // 注冊 BeanPostProcessor 的實(shí)現(xiàn)類带猴,注意看和 BeanFactoryPostProcessor 的區(qū)別
         // 此接口兩個(gè)方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個(gè)方法分別在 Bean 初始化之前和初始化之后得到執(zhí)行昔汉。這里僅僅是注冊,之后會看到回調(diào)這兩方法的時(shí)機(jī)
         registerBeanPostProcessors(beanFactory);

         // 初始化當(dāng)前 ApplicationContext 的 MessageSource拴清,國際化這里就不展開說了靶病,不然沒完沒了了
         initMessageSource();

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

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

         // 注冊事件監(jiān)聽器,監(jiān)聽器需要實(shí)現(xiàn) ApplicationListener 接口沪停。這也不是我們的重點(diǎn)煤辨,過
         registerListeners();

         // 重點(diǎn)裳涛,重點(diǎn),重點(diǎn)
         // 初始化所有的 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();
      }
   }
}

準(zhǔn)備 Bean 容器: prepareBeanFactory

之前我們說過郊闯,Spring 把我們在 xml 配置的 bean 都注冊以后,會"手動(dòng)"注冊一些特殊的 bean蛛株。

這里簡單介紹下 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) {
   // 設(shè)置 BeanFactory 的類加載器团赁,我們知道 BeanFactory 需要加載類,也就需要類加載器泳挥,
   // 這里設(shè)置為加載當(dāng)前 ApplicationContext 類的類加載器
   beanFactory.setBeanClassLoader(getClassLoader());

   // 設(shè)置 BeanExpressionResolver
   beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
   // 
   beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

   // 添加一個(gè) BeanPostProcessor然痊,這個(gè) processor 比較簡單:
   // 實(shí)現(xiàn)了 Aware 接口的 beans 在初始化的時(shí)候至朗,這個(gè) processor 負(fù)責(zé)回調(diào)屉符,
   // 這個(gè)我們很常用,如我們會為了獲取 ApplicationContext 而 implement ApplicationContextAware
   // 注意:它不僅僅回調(diào) ApplicationContextAware锹引,
   //   還會負(fù)責(zé)回調(diào) EnvironmentAware矗钟、ResourceLoaderAware 等,看下源碼就清楚了
   beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

   // 下面幾行的意思就是嫌变,如果某個(gè) bean 依賴于以下幾個(gè)接口的實(shí)現(xiàn)類吨艇,在自動(dòng)裝配的時(shí)候忽略它們,
   // 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);

   /**
    * 下面幾行就是為特殊的幾個(gè) bean 賦值东涡,如果有 bean 依賴了以下幾個(gè),會注入這邊相應(yīng)的值倘待,
    * 之前我們說過疮跑,"當(dāng)前 ApplicationContext 持有一個(gè) BeanFactory",這里解釋了第一行
    * ApplicationContext 還繼承了 ResourceLoader凸舵、ApplicationEventPublisher祖娘、MessageSource
    * 所以對于這幾個(gè)依賴,可以賦值為 this啊奄,注意 this 是一個(gè) ApplicationContext
    * 那這里怎么沒看到為 MessageSource 賦值呢渐苏?那是因?yàn)?MessageSource 被注冊成為了一個(gè)普通的 bean
    */
   beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   beanFactory.registerResolvableDependency(ResourceLoader.class, this);
   beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
   beanFactory.registerResolvableDependency(ApplicationContext.class, this);

   // 這個(gè) BeanPostProcessor 也很簡單,在 bean 實(shí)例化后菇夸,如果是 ApplicationListener 的子類琼富,
   // 那么將其添加到 listener 列表中,可以理解成:注冊 事件監(jiān)聽器
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // 這里涉及到特殊的 bean庄新,名為:loadTimeWeaver公黑,這不是我們的重點(diǎn),忽略它
   // tips: ltw 是 AspectJ 的概念,指的是在運(yùn)行期進(jìn)行織入凡蚜,這個(gè)和 Spring AOP 不一樣人断,
   //    感興趣的讀者請參考我寫的關(guān)于 AspectJ 的另一篇文章 https://www.javadoop.com/post/aspectj
   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 往往很 "智能" 就是因?yàn)樗鼤臀覀兡J(rèn)注冊一些有用的 bean朝蜘,
    * 我們也可以選擇覆蓋
    */

   // 如果沒有定義 "environment" 這個(gè) bean恶迈,那么 Spring 會 "手動(dòng)" 注冊一個(gè)
   if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
   // 如果沒有定義 "systemProperties" 這個(gè) bean,那么 Spring 會 "手動(dòng)" 注冊一個(gè)
   if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
   // 如果沒有定義 "systemEnvironment" 這個(gè) bean谱醇,那么 Spring 會 "手動(dòng)" 注冊一個(gè)
   if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}

在上面這塊代碼中暇仲,Spring 對一些特殊的 bean 進(jìn)行了處理,讀者如果暫時(shí)還不能消化它們也沒有關(guān)系副渴,慢慢往下看奈附。

初始化所有的 singleton beans

我們的重點(diǎn)當(dāng)然是 finishBeanFactoryInitialization(beanFactory); 這個(gè)巨頭了,這里會負(fù)責(zé)初始化所有的 singleton beans煮剧。

注意斥滤,后面的描述中,我都會使用初始化預(yù)初始化來代表這個(gè)階段勉盅,Spring 會在這個(gè)階段完成所有的 singleton beans 的實(shí)例化佑颇。

我們來總結(jié)一下,到目前為止草娜,應(yīng)該說 BeanFactory 已經(jīng)創(chuàng)建完成挑胸,并且所有的實(shí)現(xiàn)了 BeanFactoryPostProcessor 接口的 Bean 都已經(jīng)初始化并且其中的 postProcessBeanFactory(factory) 方法已經(jīng)得到回調(diào)執(zhí)行了。而且 Spring 已經(jīng)“手動(dòng)”注冊了一些特殊的 Bean宰闰,如 ‘environment’茬贵、‘systemProperties’ 等。

剩下的就是初始化 singleton beans 了移袍,我們知道它們是單例的解藻,如果沒有設(shè)置懶加載,那么 Spring 會在接下來初始化所有的 singleton beans咐容。

// AbstractApplicationContext.java 834

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

   // 首先舆逃,初始化名字為 conversionService 的 Bean。本著送佛送到西的精神戳粒,我在附錄中簡單介紹了一下 ConversionService路狮,因?yàn)檫@實(shí)在太實(shí)用了
   // 什么,看代碼這里沒有初始化 Bean 拔翟肌奄妨!
   // 注意了,初始化的動(dòng)作包裝在 beanFactory.getBean(...) 中苹祟,這里先不說細(xì)節(jié)砸抛,先往下看吧
   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.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
         @Override
         public String resolveStringValue(String strVal) {
            return getEnvironment().resolvePlaceholders(strVal);
         }
      });
   }

   // 先初始化 LoadTimeWeaverAware 類型的 Bean
   // 之前也說過评雌,這是 AspectJ 相關(guān)的內(nèi)容,放心跳過吧
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // 沒什么別的目的直焙,因?yàn)榈竭@一步的時(shí)候景东,Spring 已經(jīng)開始預(yù)初始化 singleton beans 了,
   // 肯定不希望這個(gè)時(shí)候還出現(xiàn) bean 定義解析奔誓、加載斤吐、注冊。
   beanFactory.freezeConfiguration();

   // 開始初始化
   beanFactory.preInstantiateSingletons();
}

從上面最后一行往里看厨喂,我們就又回到 DefaultListableBeanFactory 這個(gè)類了和措,這個(gè)類大家應(yīng)該都不陌生了吧。

由于知乎文字限制蜕煌,只能發(fā)那么多的字派阱,剩余內(nèi)容可以到

image
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市斜纪,隨后出現(xiàn)的幾起案子贫母,更是在濱河造成了極大的恐慌,老刑警劉巖傀广,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颁独,死亡現(xiàn)場離奇詭異彩届,居然都是意外死亡伪冰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門樟蠕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贮聂,“玉大人,你說我怎么就攤上這事寨辩∠判福” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵靡狞,是天一觀的道長耻警。 經(jīng)常有香客問我,道長甸怕,這世上最難降的妖魔是什么甘穿? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮梢杭,結(jié)果婚禮上温兼,老公的妹妹穿的比我還像新娘。我一直安慰自己武契,他們只是感情好募判,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布荡含。 她就那樣靜靜地躺著,像睡著了一般届垫。 火紅的嫁衣襯著肌膚如雪释液。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天装处,我揣著相機(jī)與錄音均澳,去河邊找鬼。 笑死符衔,一個(gè)胖子當(dāng)著我的面吹牛找前,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播判族,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼躺盛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了形帮?” 一聲冷哼從身側(cè)響起槽惫,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辩撑,沒想到半個(gè)月后界斜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡合冀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年各薇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片君躺。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡峭判,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棕叫,到底是詐尸還是另有隱情林螃,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布俺泣,位于F島的核電站疗认,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伏钠。R本人自食惡果不足惜横漏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贝润。 院中可真熱鬧绊茧,春花似錦、人聲如沸打掘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亡笑,卻和暖如春侣夷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仑乌。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工百拓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晰甚。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓衙传,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厕九。 傳聞我的和親對象是個(gè)殘疾皇子蓖捶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容