<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Spring進階- Spring IOC構建原理(二)IOC初始化流程

      本文是 IOC 進階第二篇。

      本文的目標就是分析 Spring 如何實現將資源配置(以xml配置為例)通過加載,解析,生成 BeanDefination 并注冊到IoC容器中的。

      閱讀源碼初學者應該會很頭大,給你們你做一個心理按摩。

      Spring 源碼代碼量大,復雜,追源碼 “迷路” 等問題非常讓人苦惱,不過從以下兩個角度入手學習源碼會輕松很多。

      • 熟悉 Spring IOC 啟動的工作原理,做到心中有數,就明白下一步源碼應該看哪里。
      • 代碼量大,找不到重點,跟著本文一步一步剖析重點。

      源碼本質上就是 java 代碼,無非是初始化構造函數,對象之間的調用等,這些都是 java 語言的基本,不會難,重點要從頂層思維看 IOC 啟動流程為何如此編寫?本文做了大量闡述,幫助你從頂層視角看清 IOC 啟動流程。通過閱讀源碼你會發現** IOC 容器在 Spring 中的技術實現不過就是一個 Java util 包的 Map 集合**。

      引言

      上文介紹了 IOC 進階之體系結構,回顧核心組件,BeanFactory 、BeanRegistry , 繼承 BeanFactory 的 ApplicationContext , ApplicationContext 是一個很重要的組件,它實現了 **ResourceLoader **便具有加載配置的能力。

      另外還有一個 BeanDefinitionReader 組件也很重要,ApplicationContext 會調用它解析 Bean 對象。 BeanDefinitionReader 與 BeanDefinition 之間是依賴關系,不要看名字腦部成繼承或者實現哦~ BeanDefinitionReader 調用 BeanDefinition 創建一個 BeanDefinition 實例。

      接下來以這幾個組件開始,介紹 IOC 初始化流程,回顧上文最后一張圖。

      IOC 容器工作流程描述

      我們從萬惡之源 Application 啟動類開始入手,在本系列文章的第一篇當中,有一個 HelloWorld 的例子中有個main 函數

      public static void main(String[] args) {
      // create and configure beans
      ApplicationContext context =
      new ClassPathXmlApplicationContext("daos.xml", "services.xml");
      // something...
      }

      **new ClassPathXmlApplicationContext(configLocations)** 構造函數被調用,做了三件事

      • 初始化 BeanFactory 和 資源加載器 ResourceLoader
      • 設置配置文件路徑
      • refresh() 初始化容器

      初始化容器是精彩之處,接著看如何初始化。

      1. 為容器刷新做準備工作,記錄啟動時間,初始化一些狀態標志。
      2. **初始化BeanFactory **: 創建并初始化 **BeanFactory**,這是容器的核心。
      3. **準備 BeanFactory: **為 **BeanFactory** 設置一些標準的、通用的特性。比如:設置 **BeanFactory** 的類加載器。
      4. **BeanFactory 后處理 : **提供一個擴展點,允許子類在 **BeanFactory** 標準化準備完成后,對其進行個性化的定制
      5. **調用 BeanFactory 后處理器: **在所有 Bean 實例化之前,對 **BeanFactory** 的元數據進行修改。
      6. **注冊 Bean 后處理器: **將所有 **BeanPostProcessor** 注冊到 **BeanFactory** 中。
      7. **初始化消息源: **為容器提供國際化(i18n)支持。
      8. **初始化事件廣播器:**為容器提供事件發布機制。
      9. ** 刷新onRefresh() : **留給子類在實例化 Bean 之前初始化一些特殊的、與上下文相關的資源。
      10. **注冊監聽器:**將所有事件監聽器注冊到事件廣播器中。
      11. **完成 BeanFactory 初始化: **實例化所有剩余的、非懶加載的單例 Bean。這是 IoC 容器最核心、最耗時的步驟。
      12. **完成刷新:**完成整個刷新過程,發布容器啟動完成事件。

      這是從源碼的角度解釋完整的 IOC 初始化流程,在本文目錄的 4、初始化的主題流程中可看見源碼。后面的源碼剖析,是基于此流程選核心重點進行深入剖析,如:XML 加載 -> 轉成 Document 對象 -> 轉成 BeanDefinition 對象 -> 封裝 BeanDefinitionHolder -> 注冊 BeanDefinitionHolder 到容器中。

      IOC 容器的工作原理

      以 XML 為例,從 Spring 源碼的角度探討 XML 配置是如何加載到 IOC 容器中。

      1、初始化的入口

      在本系列文章的第一篇當中,有一個 HelloWorld 的例子演示了如何使用 XML 啟動項目,其中 Spring 啟動入口 main 函數關鍵代碼是:

      public static void main(String[] args) {
      // create and configure beans
      ApplicationContext context =
      new ClassPathXmlApplicationContext("daos.xml", "services.xml");
      // something...
      }

      ClassPathXmlApplicationContext 類的構造函數如下:

      public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
      this(configLocations, true, (ApplicationContext)null);
      }
      public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
      // 設置Bean資源加載器
      super(parent);
      // 設置配置路徑
      this.setConfigLocations(configLocations);
      //  最終目標:IOC 容器刷新的入口 , 真正的精彩!
      if (refresh) {
      this.refresh();
      }
      }

      super(parent); 重點關注, 接下來深入挖掘它,它的作用之一是為 IOC 容器設置 Bean 資源加載器。

      2、設置資源解析器和環境

      挖掘 **super(parent) , **調用父類容器 AbstractApplicationContext 的構造方法為容器設置好 Bean 資源加載器,代碼如下:

      public AbstractApplicationContext(@Nullable ApplicationContext parent) {
      // 默認構造函數初始化容器id, name, 狀態 以及 資源解析器
      this();
      // 將父容器的Environment合并到當前容器
      this.setParent(parent);
      }

      重點這兩個 this.

      **1) ** this(); 通過 AbstractApplicationContext 默認構造函數初始化容器id, name, 狀態 以及 資源解析器。

      public AbstractApplicationContext() {
      this.logger = LogFactory.getLog(this.getClass());
      this.id = ObjectUtils.identityToString(this);
      this.displayName = ObjectUtils.identityToString(this);
      this.beanFactoryPostProcessors = new ArrayList();
      this.active = new AtomicBoolean();
      this.closed = new AtomicBoolean();
      this.startupShutdownMonitor = new Object();
      this.applicationStartup = ApplicationStartup.DEFAULT;
      this.applicationListeners = new LinkedHashSet();
      this.resourcePatternResolver = this.getResourcePatternResolver();
      }
      // Spring資源加載器
      protected ResourcePatternResolver getResourcePatternResolver() {
      return new PathMatchingResourcePatternResolver(this);
      }

      2) this.setParent(parent); 方法將父容器的 Environment 合并到當前容器。 上文我們有簡單聊過父子容器,概念不難,理解成有多個容器, IOC 容器也有繼承關系,父容器定義全局的、通用的 Bean。比如,數據庫連接池、通用的安全服務、系統級別的配置等。子容器定義自己領域的Bean 。比如:各種 Service Bean 。

      public void setParent(@Nullable ApplicationContext parent) {
      this.parent = parent;
      if (parent != null) { // 目前 if 條件不成立
      Environment parentEnvironment = parent.getEnvironment();
      if (parentEnvironment instanceof ConfigurableEnvironment) {
      this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
      }
      }
      }

      @Nullable 注解是可以為空,那么這里 parent 容器初始化自然是空的。

      3、設置配置路徑

      現在目光回看** 1、初始化入口**,里面的 ClassPathXmlApplicationContext 的 super(parent) 加載資源搞定,繼續 this.setConfigLocations(configLocations) , 看源碼:

      public void setConfigLocations(@Nullable String... locations) {
      if (locations != null) {
      Assert.noNullElements(locations, "Config locations must not be null");
      this.configLocations = new String[locations.length];
      for(int i = 0; i < locations.length; ++i) {
      // 解析配置路徑
      this.configLocations[i] = this.resolvePath(locations[i]).trim();
      }
      } else {
      this.configLocations = null;
      }
      }
      protected String resolvePath(String path) {
      // 從上一步Environment中解析
      return this.getEnvironment().resolveRequiredPlaceholders(path);
      }

      setConfigLocations 方法通過調用其父類 AbstractRefreshableConfigApplicationContext 的方法進行對 Bean定義資源文件的定位。

      進入到 ClassPathXmlApplicationContext 的初始化流程了。

      4、初始化的主體流程

      Spring IoC容器對Bean定義資源的載入是從refresh()函數開始的,refresh()是一個模板方法,refresh()方法的作用是:在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟。

      @Override
      public void refresh() throws BeansException, IllegalStateException {
      // 加鎖,防止并發刷新或關閉
      synchronized (this.startupShutdownMonitor) {
      // 1. 準備工作:設置啟動時間、校驗配置文件等
      prepareRefresh();
      // 2. 獲取Bean工廠:創建并加載Bean定義到BeanFactory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 3. 準備Bean工廠:設置工廠的標準特性,如類加載器、后處理器等
      prepareBeanFactory(beanFactory);
      try {
      // 4. 后處理Bean工廠:提供給子類的擴展點,可對BeanFactory進行自定義
      postProcessBeanFactory(beanFactory);
      // 5. 調用工廠后處理器:執行BeanFactoryPostProcessor,修改Bean的定義信息
      invokeBeanFactoryPostProcessors(beanFactory);
      // 6. 注冊Bean后處理器:注冊BeanPostProcessor,用于攔截Bean的創建過程
      registerBeanPostProcessors(beanFactory);
      // 7. 初始化消息源:用于國際化(i18n)
      initMessageSource();
      // 8. 初始化事件廣播器:用于發布事件
      initApplicationEventMulticaster();
      // 9. 刷新:提供給子類的擴展點,用于初始化特殊的Bean
      onRefresh();
      // 10. 注冊監聽器:將所有監聽器Bean注冊到事件廣播器
      registerListeners();
      // 11. 實例化Bean:實例化所有剩余的非懶加載單例Bean
      finishBeanFactoryInitialization(beanFactory);
      // 12. 完成刷新:發布刷新完成事件,容器啟動完畢
      finishRefresh();
      }
      catch (BeansException ex) {
      // 發生異常時,銷毀已創建的Bean,并重置容器狀態
      destroyBeans();
      cancelRefresh(ex);
      throw ex;
      }
      finally {
      // 重置Spring核心的公共緩存
      resetCommonCaches();
      }
      }
      }

      這個設計便是本文的主題,非常典型的資源類加載處理型的思路,頭腦中要形成如下圖的頂層思路,而不是只停留在流水式的方法上面。

      不要將這段代碼看成流水線一樣的設計,如果上圖你還是無法從頂層思路看待 IOC 容器初始化,那么下面將對頂層思路的四個重點進行分析,必然能幫到你。

      1)模板方法設計模式,模板方法中使用典型的鉤子方法。

      **refresh()** 方法本身就是一個模板方法。它定義了 Spring 容器啟動的標準、不可變的流程骨架。

      這個骨架就像一個固定的生產線: **準備 -> 加載資源 -> 配置 -> 初始化核心組件 -> 實例化 -> 完成**

      Spring 框架的設計者規定,任何一個 **ApplicationContext** 的實現(無論是基于 XML、注解還是配置類)都必須遵循這個骨架。這保證了所有 Spring 應用啟動行為的一致性。

      2)將具體的初始化加載方法插入到鉤子方法之間

      鉤子方法是啥?

      模板方法魚總帶我們在項目中寫過,都不陌生吧,抽象類定義一套固定模板,并且部分的方法是抽象的,由子類按需實現,父類固定流程,子類按需變化。鉤子方法便是父類的抽象方法。

      鉤子方法體現為**空實現或留給子類覆蓋的方法,**其中 postProcessBeanFactory(beanFactory); 是一個典型的鉤子,**AbstractApplicationContext** 不知道子類(如 **AnnotationConfigWebApplicationContext**)需要對 **BeanFactory** 做什么特殊定制。它就在這里“挖個坑”,讓子類來“填坑”。子類可以覆蓋此方法,添加自己特有的 **BeanPostProcessor** 或進行其他定制。

      3)將初始化的階段封裝,讓初始化過程有狀態

      這個設計讓初始化過程不再是一個黑盒,而是一個可以被觀察和記錄的、分階段的狀態機。

      看兩行代碼:

      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
      ...
      StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
      ...
      beanPostProcess.end();
      ...
      contextRefresh.end();
      • **applicationStartup.start("...")**:這就像在流水線的每個關鍵工位前打卡,聲明“現在進入了【XX階段】”。
      • **step.end()**:在階段完成后打卡下班,聲明“【XX階段】已完成”。

      雖然就是一個普通的記錄功能,類似于日志沒啥技術含量,但它的設計精髓是:

      • 可觀測:使用監控工具清晰地看到容器啟動卡在了哪個階段,每個階段耗時多久,極大地提升了問題排查的效率。
      • 結構清晰:代碼本身就是一份活的文檔,明確地劃分了“上下文刷新”、“Bean后置處理”等邏輯階段,讓閱讀者能快速把握代碼結構。

      4) 健壯性設計:**try/catch/finally** 的藝術

      資源加載和初始化過程極易失敗(如配置文件找不到、類定義錯誤、數據庫連不上等)。一個不健壯的設計可能導致容器處于一個半死不活的中間狀態,資源泄漏。

      這里的 try/catch/finally 是運用之模范,魚總帶我們項目時也經常寫,我們感受一下 Spring 小組和魚總的 try/catch/finally 有多少相似之處


      **try 塊:**包裹了所有核心的初始化步驟。這些步驟是原子性的——要么全部成功,要么全部失敗。保證容器從一個初始狀態,平滑過渡到一個完全就緒可用的狀態。

      **catch 塊:**具有失敗回滾的作用,非常重要,一旦try 的任何一步發生異常,它會立即執行清理和銷毀操作,將容器內部狀態標記為 “已取消”、“非活躍”,告訴外界這次啟動失敗,將異常拋出,通知啟動的調用者發生了錯誤。

      **finally 塊:**無論 try 是成功還是失敗,必定重置 Spring 核心的公共緩存

      接下來從初始化的代碼開始詳解源碼,上高速,高速入口代碼:

      // 2. 獲取Bean工廠:創建并加載Bean定義到BeanFactory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      初始化BeanFactory之obtainFreshBeanFactory

      AbstractApplicationContext的obtainFreshBeanFactory()方法調用子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資源文件的過程,代碼如下

      protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
      // 這里使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現調用子類容器的refreshBeanFactory()方法
      refreshBeanFactory();
      return getBeanFactory();
      }

      AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正調用的是其子類AbstractRefreshableApplicationContext實現的refreshBeanFactory()方法; 在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器。方法的源碼如下:

      protected final void refreshBeanFactory() throws BeansException {
      // 如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器
      if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
      }
      try {
      // 創建DefaultListableBeanFactory,并調用loadBeanDefinitions(beanFactory)裝載bean定義
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory); // 對IoC容器進行定制化,如設置啟動參數,開啟注解的自動裝配等 
      loadBeanDefinitions(beanFactory); // 調用載入Bean定義的方法,主要這里又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現調用子類容器  
      this.beanFactory = beanFactory;
      }
      catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
      }
      }
      初始化BeanFactory之loadBeanDefinitions

      這一步加載 xml 文件啦,如: {“daos.xml”, “services.xml”}

      AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正調用的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要源碼如下:

      protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
      // 創建XmlBeanDefinitionReader,即創建Bean讀取器,并通過回調設置到容器中去,容器使用該讀取器讀取Bean定義資源  
      XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
      // 配置上下文的環境,資源加載器、解析器
      beanDefinitionReader.setEnvironment(this.getEnvironment());
      beanDefinitionReader.setResourceLoader(this);
      beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 為Bean讀取器設置SAX xml解析器
      // 允許子類自行初始化(比如校驗機制),并提供真正的加載方法
      initBeanDefinitionReader(beanDefinitionReader); // 當Bean讀取器讀取Bean定義的Xml資源文件時,啟用Xml的校驗機制  
      loadBeanDefinitions(beanDefinitionReader);
      }
      protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
      // 加載XML配置方式里的Bean定義的資源
      Resource[] configResources = getConfigResources();
      if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
      }
      // 加載構造函數里配置的Bean配置文件,即{"aspects.xml", "daos.xml", "services.xml"}
      String[] configLocations = getConfigLocations();
      if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
      }
      }

      可以看到在這里創建了一個 XmlBeanDefinitionReader 對象,它實現了 AbstractBeanDefinitionReader 、BeanDefinitionReader ,看一下 AbstractBeanDefinitionReader 可以了解讀取 XML 的過程。

      AbstractBeanDefinitionReader讀取Bean定義資源

      AbstractBeanDefinitionReader的loadBeanDefinitions方法源碼如下:

      這個類并不是加載 xml 的過程,它是創建了一個 Resource 對象,分派 Resource 對象給子類來加載 Bean.

      @Override
      public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
      return loadBeanDefinitions(location, null);
      }
      public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
        "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
        }
        // 模式匹配類型的解析器,這種方式是加載多個滿足匹配條件的資源
        if (resourceLoader instanceof ResourcePatternResolver) {
        try {
        // 獲取到要加載的資源
        Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
        int count = loadBeanDefinitions(resources); // 委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能  
        if (actualResources != null) {
        Collections.addAll(actualResources, resources);
        }
        if (logger.isTraceEnabled()) {
        logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
        }
        return count;
        }
        catch (IOException ex) {
        throw new BeanDefinitionStoreException(
        "Could not resolve bean definition resource pattern [" + location + "]", ex);
        }
        }
        else {
        // 只能通過絕對路徑URL加載單個資源.
        Resource resource = resourceLoader.getResource(location);
        int count = loadBeanDefinitions(resource);
        if (actualResources != null) {
        actualResources.add(resource);
        }
        if (logger.isTraceEnabled()) {
        logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
        }
        return count;
        }
        }

      從對AbstractBeanDefinitionReader的loadBeanDefinitions方法源碼分析可以看出該方法做了以下兩件事:

      • 首先,調用資源加載器的獲取資源方法resourceLoader.getResource(location),獲取到要加載的資源。
      • 其次,真正執行加載功能是其子類XmlBeanDefinitionReader的loadBeanDefinitions方法。
      XmlBeanDefinitionReader加載Bean定義資源

      繼續看子類XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean文件的資源定義以后的載入過程。

      通過源碼分析,載入Bean定義資源文件的最后一步是將Bean定義資源轉換為Document對象,該過程由documentLoader實現, documentLoader 內部真正進行 IO 讀取。

      /**
      * 本質上是加載XML配置的Bean。
      * @param inputSource the SAX InputSource to read from
      * @param resource the resource descriptor for the XML file
      */
      protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
      try {
      // doLoadDocument 方法內部是真正進行 IO 讀取 XML 
      Document doc = doLoadDocument(inputSource, resource); // 將Bean定義資源轉換成Document對象
      int count = registerBeanDefinitions(doc, resource); // 對 Bean 定義的解析過程
      if (logger.isDebugEnabled()) {
      logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
      }
      catch (BeanDefinitionStoreException ex) {
      throw ex;
      }
      catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
      "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
      }
      catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
      "XML document from " + resource + " is invalid", ex);
      }
      catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
      "Parser configuration exception parsing XML from " + resource, ex);
      }
      catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
      "IOException parsing XML document from " + resource, ex);
      }
      catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
      "Unexpected exception parsing XML document from " + resource, ex);
      }
      }
      // 使用配置的DocumentLoader加載XML定義文件為Document.
      protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
      return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
      getValidationModeForResource(resource), isNamespaceAware());
      }
      DocumentLoader將Bean定義資源轉換為Document對象

      這一步是將 XML 文件的符號轉換成 Document 對象,這一步的內部有兩步,一解析 XML, 二解析 Document , 然后才是轉換。

      DocumentLoader **將 Bean定義資源 **轉換成 **Document對象 **的源碼如下:

      // 使用標準的JAXP將載入的Bean定義資源轉換成document對象
      @Override
      public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
      // 創建文件解析器工廠
      DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
      if (logger.isTraceEnabled()) {
      logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
      }
      // 創建文檔解析器
      DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
      return builder.parse(inputSource); // 解析
      }
      protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
      throws ParserConfigurationException {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setNamespaceAware(namespaceAware);
      // 設置解析XML的校驗
      if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
      factory.setValidating(true);
      if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
      // Enforce namespace aware for XSD...
      factory.setNamespaceAware(true);
      try {
      factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
      }
      catch (IllegalArgumentException ex) {
      ParserConfigurationException pcex = new ParserConfigurationException(
      "Unable to validate using XSD: Your JAXP provider [" + factory +
      "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
      "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
      pcex.initCause(ex);
      throw pcex;
      }
      }
      }
      return factory;
      }

      該解析過程調用JavaEE標準的JAXP標準進行處理。

      至此Spring IoC容器根據定位的Bean定義資源文件,將其加載讀入并轉換成為Document對象過程完成。

      接下來我們要繼續分析Spring IoC容器將載入的Bean定義資源文件轉換為Document對象之后,是如何將其解析為Spring IoC管理的Bean對象并將其注冊到容器中的。

      XmlBeanDefinitionReader解析載入的 Bean 定義資源文件

      這一步是對 XML 文件解析。

      XmlBeanDefinitionReader 類中的 doLoadBeanDefinitions 方法是從特定XML文件中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之后將其轉換為Document對象,接下來調用registerBeanDefinitions 啟動Spring IoC容器對Bean定義的解析過程,registerBeanDefinitions方法源碼如下:

      // 按照Spring的Bean語義要求將Bean定義資源解析并轉換為容器內部數據結構 
      public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
      BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
      int countBefore = getRegistry().getBeanDefinitionCount();
      // 解析過程入口,這里使用了委派模式,具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成  
      documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
      return getRegistry().getBeanDefinitionCount() - countBefore;  // 返回此次解析了多少個對象
      }
      // 創建BeanDefinitionDocumentReader對象,解析Document對象  
      protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
      return BeanUtils.instantiateClass(this.documentReaderClass);
      }
      /**
      * Create the {@link XmlReaderContext} to pass over to the document reader.
      */
      public XmlReaderContext createReaderContext(Resource resource) {
      return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
      this.sourceExtractor, this, getNamespaceHandlerResolver());
      }

      Bean定義資源的載入解析分為以下兩個過程:

      • 首先,通過調用XML解析器將Bean定義資源文件轉換得到Document對象,但是這些Document對象并沒有按照Spring的Bean規則進行解析。這一步是載入的過程
      • 其次,在完成通用的XML解析之后,按照Spring的Bean規則對Document對象進行解析。

      Document對象解析的過程是在接口BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader 中實現的。

      DefaultBeanDefinitionDocumentReader對Bean定義的Document對象解析

      對 Document 對象進行解析,轉換成 Document 對象。

      BeanDefinitionDocumentReader接口通過registerBeanDefinitions方法調用其實現類DefaultBeanDefinitionDocumentReader對Document對象進行解析,解析的代碼如下:

      @Override
      public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
      this.readerContext = readerContext;
      doRegisterBeanDefinitions(doc.getDocumentElement());
      }
      // 注冊<beans/>配置的Beans
      @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
      protected void doRegisterBeanDefinitions(Element root) {
      // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
          // keep track of the current (parent) delegate, which may be null. Create
          // the new (child) delegate with a reference to the parent for fallback purposes,
          // then ultimately reset this.delegate back to its original (parent) reference.
          // this behavior emulates a stack of delegates without actually necessitating one.
          BeanDefinitionParserDelegate parent = this.delegate;
          this.delegate = createDelegate(getReaderContext(), root, parent);
          if (this.delegate.isDefaultNamespace(root)) {
          String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
          if (StringUtils.hasText(profileSpec)) {
          String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
          profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
          // We cannot use Profiles.of(...) since profile expressions are not supported
          // in XML config. See SPR-12458 for details.
          if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
          if (logger.isDebugEnabled()) {
          logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
          "] not matching: " + getReaderContext().getResource());
          }
          return;
          }
          }
          }
          preProcessXml(root);
          parseBeanDefinitions(root, this.delegate); // 從Document的根元素開始進行Bean定義的Document對象  
          postProcessXml(root);
          this.delegate = parent;
          }
      BeanDefinitionParserDelegate解析Bean定義資源文件生成BeanDefinition

      將 Ducument 對象轉換為 BeanDefinition 對象。

      /**
      * Parse the elements at the root level in the document:
      * "import", "alias", "bean".
      * @param root the DOM root element of the document
      */
      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);
      }
      }
      private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
      // 如果元素節點是<Import>導入元素,進行導入解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
        }
        // 如果元素節點是<Alias>別名元素,進行別名解析 
          else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
          processAliasRegistration(ele);
          }
          // 如果元素節點<Bean>元素, 按照Spring的Bean規則解析元素  
            else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
            }
            // 如果元素節點<Beans>元素,即它是嵌套類型的
              else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
              // 遞歸解析
              doRegisterBeanDefinitions(ele);
              }
              }

      然后是把 BeanDefinition 封裝成 BeanDefinitionHolder 的源碼。BeanDefinitionHolder 是一個信息更豐富的“工作載體”

      protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
      BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
      if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
      // 注冊最終的裝飾實例
      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));
      }
      }

      得到一個 BeanDefinitionHolder 對象就代表 Bean 已經正式解析好了,可以被注入到容器。

      解析過后的BeanDefinition在IoC容器中的注冊

      這一步是要把 BeanDefinitionHolder 對象注冊到容器中。

      調用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注冊解析的Bean,BeanDefinitionReaderUtils的注冊的源碼如下:

      // 通過BeanDefinitionRegistry將BeanDefinitionHolder注冊到BeanFactory
      public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {
      // Register bean definition under primary name.
      String beanName = definitionHolder.getBeanName();
      registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
      // Register aliases for bean name, if any.
      String[] aliases = definitionHolder.getAliases();
      if (aliases != null) {
      for (String alias : aliases) {
      registry.registerAlias(beanName, alias);
      }
      }
      }

      調用BeanDefinitionReaderUtils向IoC容器注冊解析的BeanDefinition時,真正完成注冊功能的是DefaultListableBeanFactory。

      DefaultListableBeanFactory向IoC容器注冊解析后的BeanDefinition

      這里能看到 Spirng IOC 容器技術實現的真面目,本質上是一個 Map<String, BeanDefinition> .

      IOC 容器本質上就是一個 beanDefinitionMap, 注冊即將BeanDefinition put到map中

      /** Map of bean definition objects, keyed by bean name. */
      private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
        /** Map from bean name to merged BeanDefinitionHolder. */
        private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
          @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(beanDefinition.getResourceDescription(), beanName,
          "Validation of bean definition failed", ex);
          }
          }
          BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
          // 如果已經注冊
          if (existingDefinition != null) {
          // 檢查是否可以覆蓋
          if (!isAllowBeanDefinitionOverriding()) {
          throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
          }
          else if (existingDefinition.getRole() < beanDefinition.getRole()) {
          // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
          if (logger.isInfoEnabled()) {
          logger.info("Overriding user-defined bean definition for bean '" + beanName +
          "' with a framework-generated bean definition: replacing [" +
          existingDefinition + "] with [" + beanDefinition + "]");
          }
          }
          else if (!beanDefinition.equals(existingDefinition)) {
          if (logger.isDebugEnabled()) {
          logger.debug("Overriding bean definition for bean '" + beanName +
          "' with a different definition: replacing [" + existingDefinition +
          "] with [" + beanDefinition + "]");
          }
          }
          else {
          if (logger.isTraceEnabled()) {
          logger.trace("Overriding bean definition for bean '" + beanName +
          "' with an equivalent definition: replacing [" + existingDefinition +
          "] with [" + beanDefinition + "]");
          }
          }
          // 覆蓋
          this.beanDefinitionMap.put(beanName, beanDefinition);
          }
          else {
          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<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            removeManualSingletonName(beanName);
            }
            }
            else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
            }
            //重置所有已經注冊過的BeanDefinition的緩存  
            this.frozenBeanDefinitionNames = null;
            }
            if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
            }
            else if (isConfigurationFrozen()) {
            clearByTypeCache();
            }
            }

      附上一張 IOC 啟動流程圖

      在這里插入圖片描述

      參考文章

      https://pdai.tech/md/spring/spring-x-framework-ioc-source-2.html

      https://blog.csdn.net/qq_36212439/article/details/82749963

      https://juejin.cn/post/6973884466171215908

      https://juejin.cn/post/6844903838743265294

      https://blog.csdn.net/hjing123/article/details/104867343

      http://www.rzrgm.cn/wl20200316/p/12522993.html

      posted @ 2025-11-04 19:09  yxysuanfa  閱讀(5)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 波多野结衣免费一区视频| 亚洲欧美综合精品成| 无码av岛国片在线播放| 日韩有码av中文字幕| 中文乱码人妻系列一区二区| 国产精品一区二区三区蜜臀| 亚洲一区在线观看青青蜜臀| 久久综合久色欧美综合狠狠| 精品一区二区三区不卡| 久久人妻国产精品| 又黄又无遮挡AAAAA毛片| 国产午夜精品福利91| 婷婷五月综合激情| 日本免费人成视频在线观看| 高清无打码一区二区三区| 国产一区二区高清不卡| 亚洲国产精品人人做人人爱| √新版天堂资源在线资源| 国产精品亚洲片夜色在线| 亚洲国产精品一区二区第一页| 国产精品成人自产拍在线| 国产精品久久久久乳精品爆| 免费看视频的网站| 久青草国产在视频在线观看| 熟女人妻视频| 蜜臀91精品高清国产福利| 亚洲日韩久热中文字幕| 日本高清一区免费中文视频| 香蕉亚洲欧洲在线一区| 国产成人 综合 亚洲欧洲| 色欲AV无码一区二区人妻| 国产精品美女一区二三区| 欧美牲交a欧美牲交aⅴ免费真| 蜜桃伦理一区二区三区| 国产久免费热视频在线观看| 国内自拍偷拍一区二区三区 | 国产成人自拍小视频在线| 亚洲国产超清无码专区| 亚洲国产av无码精品无广告| 丝袜美腿视频一区二区三区| 狠狠婷婷色五月中文字幕|