1. 程式人生 > >Spring Framework--ApplicationComtext(3)詳解AbstractApplicationContext.refresh()--

Spring Framework--ApplicationComtext(3)詳解AbstractApplicationContext.refresh()--

前言

上篇部落格主要以ClassPathXmlApplicationContext為線,debug走了一遍建構函式。本篇嚴格來說是上一篇部落格的繼續,因此本文將繼續以ClassPathXmlApplicationContext為線走進AbstractApplicationContext.refresh()。

概述

​ refresh方法位於ConfigurableApplicationContext介面類中,被AbstractApplicationContext抽象類繼承,初步實現了ApplicationContext的一般功能,並且抽象類中使用了模板方法模式,給以後要實現的子類提供了統一的模板。

// 注程式碼中的中文只是單純的翻譯,以及簡單的個人理解。

/**
     * Load or refresh the persistent representation of the configuration,
     * which might an XML file, properties file, or relational database schema.
     * <p>As this is a startup method, it should destroy already created singletons
     * if it fails, to avoid dangling resources. In other words, after invocation
     * of that method, either all or no singletons at all should be instantiated.
     * @throws
BeansException if the bean factory could not be initialized * @throws IllegalStateException if already initialized and multiple refresh * attempts are not supported */
/** * 載入或則重新整理持久表示的配置,這個配置可能來自一個xml檔案,配置檔案或則關係型資料庫物件的集合。由於這是一個 * 啟動方法,它應該銷燬已經建立的單例。 如果失敗了,就避免了懸空的資源。 換句話說,在呼叫該方法之後,應該 * 例項化所有或者全部的no singletons(這裡不知道怎麼翻譯)。 */
void refresh() throws BeansException, IllegalStateException; public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 為重新整理準備上下文,獲取容器的當時時間,同時給容器設定同步標識 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 告訴子類需要重新整理的內部bean工廠 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 在上下文中準備bean工廠為了之後使用。 // 為bean工廠配置容器特徵,例如類載入器,事件處理器等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 在上下文子類中,允許對bean工廠進行後期處理 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 在上下文中,呼叫工廠處理器註冊成為bean // 呼叫bean工廠的後處理器,這些處理器是在bean定義中想容器註冊的。 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 註冊用於攔截bean建立的bean處理器 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 為上下文初始化訊息資源 initMessageSource(); // Initialize event multicaster for this context. // 初始化上下文中的事件多路廣播--->事件多播器 // 初始化上下文中的事件機制 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 初始化指定context子類中的特殊bean onRefresh(); // Check for listener beans and register them. // 註冊並檢查bean監聽器 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 初始化剩下的非延遲載入(non-lazy-init)單例beans finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 最後一步,釋出相關的容器事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singleßtons to avoid dangling resources. // 銷燬已經建立的單例bean destroyBeans(); // Reset 'active' flag. // 取消refresh操作,充值‘activity’標誌位 cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

正文

  1. prepareRefresh();

    先來看看一下prepareRefresh的時序圖

    這裡寫圖片描述

    再看看程式碼

    /**
     * Prepare this context for refreshing, setting its startup date and
     * active flag as well as performing any initialization of property sources.
     */
    /*
    為重新整理準備上下文,設定它的開始時間和活動標誌以及執行所有屬性資源的初始化。
    */
    protected void prepareRefresh() {
        // 設定啟動時間
            this.startupDate = System.currentTimeMillis();
            // 設定關閉標誌位false,執行標誌位true.
            // close 和activityAtomicBoolean 
        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();
    
        // Validate that all properties marked as required are resolvable
        // see ConfigurablePropertyResolver#setRequiredProperties
           // 驗證被標記為必要屬性的可解析性
           getEnvironment().validateRequiredProperties();
           // 上面方法單步進去,看到如下程式碼。這裡對requiredProperties屬性的set還不知道在哪。
           // public void validateRequiredProperties() {
    // MissingRequiredPropertiesException ex = new  MissingRequiredPropertiesException();
    //       for (String key : this.requiredProperties) {
    //           if (this.getProperty(key) == null) {
    //               ex.addMissingRequiredProperty(key);
    //           }
    //       }
    //       if (!ex.getMissingRequiredProperties().isEmpty()) {
    //           throw ex;
    //       }
    //   }
    
    
        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
           // 在早期就建立起ApplicationEvent事件容器,一旦多路廣播可用就可以釋出了。
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }   

  2. obtainFreshBeanFactory()—— 告訴子類重新整理內部bean工廠

      protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    
        /**
         * This implementation performs an actual refresh of this context's underlying
         * bean factory, shutting down the previous bean factory (if any) and
         * initializing a fresh bean factory for the next phase of the context's lifecycle.
         */
        // 這個實現執行一個上下文的底層bean工廠重新整理,關閉之前已有的bean工廠,併為上下文生命週期的下一個階段初始化bean工廠
        refreshBeanFactory(); 
    
        // 獲取bean工廠
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
        if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }ß
            return beanFactory;
        }

    ##### 2.1 refreshBeanFactory()

    首先來看看時序圖:

    這裡寫圖片描述

        /**
         * This implementation performs an actual refresh of this context's underlying
         * bean factory, shutting down the previous bean factory (if any) and
         * initializing a fresh bean factory for the next phase of the context's lifecycle.
         */
        @Override
        protected final void refreshBeanFactory() throws BeansException {
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
                customizeBeanFactory(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);
            }
        }

    refreshBeanFactory中主要分為以下幾步:

    • 判斷是否之前是否存在bean工廠,如果存在,將bean工廠、快取等資訊清空
    • 建立一個DefaultListableBeanFactory工廠
    • 定製當前bean工廠,能否進行重寫及迴圈引用 –customizeBeanFactory(beanFactory)
    • 載入所有bean到beanFactory

      2.2 loadBeanDefinitions(beanFactory)

      在2.1中refreshBeanFactory中,loadBeanDefinitions是一個抽象方法,其具體實現交由子類。現在來看看AbstractXmlApplicationContext是如何實現的。

      /**
       * Loads the bean definitions via an XmlBeanDefinitionReader.
       * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
       * @see #initBeanDefinitionReader
       * @see #loadBeanDefinitions
       */ 
      // 通過XmlBeanDefinitionReader載入bean定義
      @Override
      protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
          // Create a new XmlBeanDefinitionReader for the given BeanFactory.
          XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 建立一個XmlBeanDefinitionReader
      
          // Configure the bean definition reader with this context's
          // resource loading environment.
          beanDefinitionReader.setEnvironment(this.getEnvironment()); // 設定當前的environment的物件
          beanDefinitionReader.setResourceLoader(this); // 設定對應的ResourceLoader.ApplicationContext是ResourceLoader的子類
          beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
      
          // Allow a subclass to provide custom initialization of the reader,
          // then proceed with actually loading the bean definitions.
            // 鉤子方法,子類不過載就不做任何操作
          initBeanDefinitionReader(beanDefinitionReader);    
          loadBeanDefinitions(beanDefinitionReader);   // 載入對應BeanDefinition物件
      }
      2.2.1 loadBeanDefinitions

      上面loadBeanDefinitions對應的實現

      // 在AbstractXmlApplicationContext中loadBeanDefinitions的實現
      protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
      
          Resource[] configResources = getConfigResources();   // 為空
          if (configResources != null) {
              reader.loadBeanDefinitions(configResources);
          }
          // 在AbstractRefreshableConfigApplicationContext中的getConfigLocations獲取配置路徑
          String[] configLocations = getConfigLocations(); // configLocations=/org/springframework/context/support/test/context*.xml
          if (configLocations != null) {
                  // 呼叫xml
              reader.loadBeanDefinitions(configLocations);
          }
      }
      
      // 在AbstractBeanDefinitionReader中loadBeanDefinitions()方法解析lications陣列
      @Override
      public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
          Assert.notNull(locations, "Location array must not be null");
          int counter = 0;
          for (String location : locations) {
              counter += loadBeanDefinitions(location);
          }
          return counter;
      }

      具體的載入beanDefinition的實現在AbstractBeanDefinitionReader中

      下面是它的時序圖

      這裡寫圖片描述

      // 上面for迴圈中loadBeanDefinitions(location)方法的在AbstractBeanDefinitionReader中的
       public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
              ResourceLoader resourceLoader = getResourceLoader();  // 獲取資源載入器
              if (resourceLoader == null) {
                  throw new BeanDefinitionStoreException(
                          "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
              }
      
              if (resourceLoader instanceof ResourcePatternResolver) {
                  // Resource pattern matching available.
                  try {
                      Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);   // 將給定的位置模式解析為資源物件
                      int loadCount = loadBeanDefinitions(resources);
                      if (actualResources != null) {
                          for (Resource resource : resources) {
                              actualResources.add(resource);
                          }
                      }
                      if (logger.isDebugEnabled()) {
                          logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                      }
                      return loadCount;
                  }
                  catch (IOException ex) {
                      throw new BeanDefinitionStoreException(
                              "Could not resolve bean definition resource pattern [" + location + "]", ex);
                  }
              }
              else {
                  // Can only load single resources by absolute URL.
                  Resource resource = resourceLoader.getResource(location);
                  int loadCount = loadBeanDefinitions(resource);
                  if (actualResources != null) {
                      actualResources.add(resource);
                  }
                  if (logger.isDebugEnabled()) {
                      logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                  }
                  return loadCount;
              }
          }

      在上面的getResources()方法之後又呼叫了一下這些方法:

      這裡寫圖片描述
      由上圖可知從refresh—>getResourses—>doRetrievenMatchingFiles.其中從getResourses到doRetrievenMatchingFiles主要做了以下這些事:

      ​ 1) 看location裡面是否以”classpath*:“,”war:“,”*/“開頭,會做一系列事(這裡流程沒有進去)。這裡主要在PathMatchingResourcePatternResolver#getResources(String locationPattern)

      ​ 2) 找到location的上層路徑rootDirResource,同時分離出外層子路徑subPattern

      ​ 3)轉而根據rootDirResource獲取完整檔案路徑rootDir

      ​ 4)再將完整檔案路徑rootDir和外層自路徑subPattern拼接得到fullPattern

      ​ 5)最後找出rootDir下所有檔案的全路徑進行遍歷,與fullPattern進行匹配.

      protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
          if (logger.isDebugEnabled()) {
              logger.debug("Searching directory [" + dir.getAbsolutePath() +
                      "] for files matching pattern [" + fullPattern + "]");
          }
          File[] dirContents = dir.listFiles();
          if (dirContents == null) {
              if (logger.isWarnEnabled()) {
                  logger.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
              }
              return;
          }
          Arrays.sort(dirContents);
          for (File content : dirContents) {
              String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
              if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
                  if (!content.canRead()) {
                      if (logger.isDebugEnabled()) {
                          logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
                                  "] because the application is not allowed to read the directory");
                      }
                  }
                  else {
                      doRetrieveMatchingFiles(fullPattern, content, result);
                  }
              }
              if (getPathMatcher().match(fullPattern, currPath)) {
                  result.add(content);
              }
          }
      }

      上述方法中匹配方法呼叫 getPathMatcher().match(fullPattern, currPath)

      最終匹配演算法:

        AntPathMatcher#doMatch(
                String pattern, 
                String path,boolean fullMatch,
                @Nullable Map<String, String> uriTemplateVariables) 

      匹配演算法的大致步驟(中間省略了一些):

      ​ 1)將需要匹配的兩個路徑按照路徑分割符分割,得到兩個有序陣列pathDirs(路徑下的子檔案全路徑分割後的字元陣列),pattDirs(要匹配的模式分割後的字串陣列)

      ​ 2)在得到pttDirs後會去看這裡面是否存在潛在的匹配,主要是為了去匹配正則表示式中的{ ‘*’, ‘?’, ‘{’ },若不存在潛在的匹配則返回false

      ​ 3)遍歷pattDirs,如果包含”**“,直接跳出遍歷;記錄當前位置,與pathDirs對應位置的字串進行比較,不同則返回false

      ​ 4)第3步全部匹配結束後,

      ​ 在我們的路徑中

      pattern = /Users/chonglou/IdeaProject/spring-framework/spring-context/out/test/resources/org/springframework/context/support/test/context*.xml
      path = /Users/chonglou/IdeaProject/spring-framework/spring-context/out/test/resources/org/springframework/context/support/test/contextC.xml

      ​ 最後只是去看看我們的模式匹配路徑和全路徑是否都是以”/“結束,顯然上述兩個均沒有以”/“結束,返回false

      這裡我們只是粗略介紹,之後,會詳細介紹。

      注:
      在AntPathMatcherTests測試用例中,還暫時未發現通過
      
        - {spring:[a-z]+} matches the regexp [a-z]+ as a path variable named "spring"
      
      也就是通過花括弧,這種形式去匹配xml路徑的。
      
      不過卻發現通過
      
        result = pathMatcher.extractUriTemplateVariables(
        "{symbolicName:[\\w\\.]+}-sources-{version:[\\d\\.]+}-{year:\\d{4}}{month:\\d{2}}{day:\\d{2}}.jar","com.example-sources-1.0.0-20100220.jar");
      
      這樣去解析jar包或則url路徑的。之後我們會有單獨的部落格進行講解。
      

總結

  • refresh 的一些方法但看確實能懂一些,但是要串起來,感覺有點難啊。加油吧。。。
  • prepareBeanFactory()看了一部分了,後面其他方法也會陸續跟上。

相關推薦

Spring Framework--ApplicationComtext(3)AbstractApplicationContext.refresh()--

前言 上篇部落格主要以ClassPathXmlApplicationContext為線,debug走了一遍建構函式。本篇嚴格來說是上一篇部落格的繼續,因此本文將繼續以ClassPathXmlApplicationContext為線走進AbstractAppli

Spring Boot(3)---Spring Boot啟動器Starter

Spring Boot的啟動器Starter詳解   Spring Boot 簡化了 Spring 應用開發,不需要配置就能執行 Spring 應用, Spring Boot 管理 Spring 容器、第三 方外掛 並提供很多預設系統級的服務。大部 Spring 應用,

springcloud系列—Zuul—第5章-3: Spring Cloud Zuul過濾器

資料參考:《Spring Cloud 微服務實戰》 目錄 過濾器 請求生命週期 核心過濾器 pre過濾器 route過濾器 post過濾器 過濾器     在Spring Cloud Zuul 中實現的過濾器必須包含4個基本特徵:過濾型別、

elasticSearch+spring 整合 maven依賴

elastics tail -s clas 1.0 too group nbsp snapshot 摘自:http://www.mayou18.com/detail/nTxPQSyu.html 【Elasticsearch基礎】elasticSearch+spring 整合

spring配置文件

type ransac 啟動 toc ref 意圖 new admins 裝配 轉自: http://book.51cto.com/art/201004/193743.htm 此處詳細的為我們講解了spring2.5的實現原理,感覺非常有用

筆記:Spring Cloud Ribbon RestTemplate

定義 方法 template 包含 mediatype pst ron request temp 詳細介紹RestTemplate 針對幾種不同請求類型和參數類型的服務調用實現,示例代碼中的 restTemplate 都是通過Spring 註

spring 依賴註入

3.1 代碼 peak mxml 手動 初始化 org contex thum 當我們對一個javaBean進行實例化時,在原本的情況下我們會選擇新建一個接口,然後進行實例化,為了進一步降低耦合度我們還會使用工廠模式進行封裝。 例: 當我們想要去造,Chinese、Amer

Spring MVC整合Velocity

springmvcvelocity一、Velocity簡介Velocity是一個基於java的模板引擎(template engine)。它允許任何人僅僅簡單的使用模板語言(template language)來引用由java代碼定義的對象。當Velocity應用於web開發時,界面設計人員可以和java程序

Spring:jar包

bsp bject 表達 title node unit 調度 緩存 support org.springframework.aop ——Spring的面向切面編程,提供AOP(面向切面編程)的實現 org.springframework.asm——spring 2.

Spring Boot 之 HelloWorld

www repos lookup dex lns jar nap put epo SpringBoot介紹~<暫時假裝有> 配置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht

java retry(重試) spring retry, guava retrying

warn llb 序號 @service call 模板 out ble 結束 系列說明 java retry 的一步步實現機制。 java-retry 源碼地址 情景導入 簡單的需求 產品經理:實現一個按條件,查詢用戶信息的服務。 小明:好的。沒問題。 代碼 Use

Spring MVC @RequestMapping註解(2)

並不是 value get ecif 使用 .com java代碼 處理方法 分開 @RequestMapping 參數說明   value:定義處理方法的請求的 URL 地址。(重點)   method:定義處理方法的 http method 類型,如 GET、POST

Spring MVC之@RequestMapping Spring MVC之@RequestMapping

轉自原文 Spring MVC之@RequestMapping 詳解 引言: 前段時間專案中用到了REST風格來開發程式,但是當用POST、PUT模式提交資料時,發現伺服器端接受不到提交的資料(伺服器端引數繫結沒有加 任何註解),查看了提交方式為application/json, 而且伺服器端通過requ

Spring Boot異常處理

在Spring MVC異常處理詳解中,介紹了Spring MVC的異常處理體系,本文將講解在此基礎上Spring Boot為我們做了哪些工作。下圖列出了Spring Boot中跟MVC異常處理相關的類。 Spring Boot在啟動過程中會根據當前環境進行AutoConfigurat

spring-boot入門程式

1.建立一個普通的maven專案,專案名為boot-learnning 2.在pom.xml新增parent依賴 <parent> <groupId>org.springframework.boot</groupId> <

spring boot之配置

spring boot支援的配置檔案 spring boot支援兩種型別的配置檔案,一種是傳統的預設配置檔案application.properties ,還有一種是現在被廣泛推薦使用的YAML檔案。書寫上properties是採用鍵值對的形式來表示,而YAML是以類似大綱的縮排形式,這

Spring自動裝配Bean

att spa bject 快速 個數 就會 否則 strong pro 1. Auto-Wiring ‘no’ 2. Auto-Wiring ‘byName’ 3. Auto-Wiring ‘byType 4. Auto-Wirin

Spring事務傳播行為

前言 Spring在TransactionDefinition介面中規定了7種類型的事務傳播行為。事務傳播行為是Spring框架獨有的事務增強特性,他不屬於的事務實際提供方資料庫行為。這是Spring為我們提供的強大的工具箱,使用事務傳播行可以為我們的開發工作提供許多便利。但是人們對他的誤解也頗

centos7.5下yum 安裝mariadb10.3

目錄   前言 1、刪除舊版本 2,建立 MariaDB.repo 3、安裝 install MariaDB 4、 安裝完成MariaDB,首先啟動MariaDB 5、 設定開機啟動 systemctl 常用命令: 6、 接下來進行MariaDB的相