Spring源碼深度解析(二)

Spring源碼深度解析(二)

一. 主要內容

  1. BeanFactory體系設計
  2. 核心接口
  3. BeanDefinition加載流程
  4. 設計思想總結

二. BeanFactory體系設計

BeanFactory是Spring中十分重要的接口,也是Spring IOC容器的頂級接口。它基於工廠模式,定義了最基本的IOC容器的功能,如獲取Bean實例、查看Bean的類型和查看Bean是否存在等:
在這裏插入圖片描述

在BeanFactory的基礎上擴展了很多接口,每個接口都有各自的功能。BeanFactory的繼承體系如下:
在這裏插入圖片描述

三. 各種BeanFactory

  1. ListableBeanFactory:可以根據條件列出Bean的清單

    在這裏插入圖片描述

  2. AutowireCapableBeanFactory:提供了Bean自動注入的支持。

  3. HierarchicalBeanFactory:提供了容器繼承的支持,可以維護一個parentBeanFactory父容器的引用,並通過它獲取到父容器中的Bean。

    在這裏插入圖片描述

  4. ConfigurableBeanFactory:可對BeanFactory進行各種配置

  5. DefaultListableBeanFactory:BeanFactory的默認實現,實現了上述所有接口的功能,是BeanFactory的集大成者。

  6. XmlBeanFactory:DefaultListableBeanFactory的子類,繼承了DefaultListableBeanFactory的所有功能,並在此基礎上擴展了XML文件相關的定製化處理。

可以看出,Spring很好地貫徹了面向接口編程的原則,幾乎所有的重要的類上面都定義了接口。而且,通過接口的繼承,可以靈活地擴展出不同功能的接口。

四. 核心接口

  1. BeanDefinition:對一個Bean實例的描述,包括其類型、屬性、構造器、依賴等。

    之前看過一個比喻:BeanFactory相當於一個汽車製造工廠,Bean對應於工廠中造出的各種車,而BeanDefinition就是每種車的圖紙,描述了車的品牌、型號、所需零件、性能參數等。

  2. BeanDefinitionRegistry:BeanDefinition註冊中心,定義了對 BeanDefinition 的各種增刪改操作。

    在這裏插入圖片描述

  3. AliasRegistry:別名註冊中心。Spring支持爲一個Bean定義多個別名。

  4. SingletonBeanRegistry:單例Bean註冊中心。Spring會緩存所有Singleton類型的Bean,每次獲取到的都是同一個。

    BeanDefinition在定義了Bean的Scope,常用的有Singleton和Prototype,還可以擴展Scope接口進行自定義。

    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    
  5. Resource:Spring對底層資源的封裝,抽象了所有Spring內部可以使用到的底層資源,並提供了一些相關的查詢和操作方法

    在這裏插入圖片描述

    對於不同來源的Resource,Spring都提供了對應的實現類,常用的有文件系統資源FileSystemResource和類路徑資源ClassPathResource等。

五. BeanDefinition加載流程解析

BeanDefinition加載是Spring最重要的一個步驟,只有加載了所有定義的BeanDefinition後,才能根據其創建指定Bean,注入依賴,並完成後續一系列功能。BeanDefinition加載是在容器啓動時自動完成的。

書中以XmlBeanFactory爲例,講解了BeanDefinition的加載和註冊流程。雖然現在都以註解方式定義Bean,XML文件方式很少使用,但兩種方式只是BeanDefinition的來源不同,核心處理流程是類似的,因此仍然以書中的內容爲準。至於註解方式加載BeanDefinition的流程,如果感興趣可以看下AnnotationConfigApplicationContext類。

XmlBeanFactory加載BeanDefinition的流程大致可以分爲三個核心步驟:

  1. 資源的定位
  2. 將資源文件的解析爲BeanDefinition集合
  3. 將解析好的BeanDefinition注入到容器中

六. 資源定位

可以顯式指定BeanDefinition所在資源的位置,如書中代碼所示:

Resource resource = new ClassPathResource("applicationContext.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);

這樣顯式Resource的方式省去了資源定位的步驟,但是,實際上沒有這麼用的,現在使用的都是ApplicationContext接口,其加載資源的處理在AbstractRefreshableApplicationContext#refreshBeanFactory()中,後面章節有具體敘述。

七. BeanDefinition解析

不管基於何種方式,現在拿到了BeanDefinition所在的資源文件Resource,則創建XmlBeanFactory,執行BeanDefinition解析的工作。

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  super(parentBeanFactory);

  //加載配置文件
  this.reader.loadBeanDefinitions(resource);
}

XmlBeanFactory在維護了一個XmlBeanDefinitionReader實例,其功能就是從XML配置文件中解析出BeanDefinition。XmlBeanDefinitionReader是BeanDefinitionReader接口的實現類,BeanDefinitionReader接口的作用就是解析BeanDefinition。

解析過程中的核心類:

  1. XmlBeanFactory:XML形式的Bean工廠。
  2. XmlBeanDefinitionReader:負責從XML配置文件中讀取BeanDefinition。
  3. DocumentLoader:專注於XML解析,將執行的XML文件解析成一個Document對象。
  4. BeanDefinitionDocumentReader:針對DocumentLoader解析好的Document,從中加載出BeanDefinition,並註冊到容器中。
  5. BeanDefinitionParserDelegate:BeanDefinitionDocumentReader內部維護的委託對象,創建BeanDefinition實例並對其進行包裝。
  6. BeanDefinitionReaderUtils:最終執行實際的BeanDefinition的註冊。

XmlBeanFactory並沒有自己完成BeanDefinition解析的工作,而是委託給各種不同的類去處理,每個類只關注一項具體的功能,這也是單一職責原則的很好的體現。

加載BeanDefinition流程:

在這裏插入圖片描述

八. BeanDefinition註冊

九. 設計思想總結

  1. 工廠模式

  2. 面向接口編程

  3. 單一職責原則

  4. 策略模式:BeanDefinitionReader的作用是讀取BeanDefinition。根據BeanDefinition的來源不同,Spring定義了不同的實現類(XmlBeanDefinitionReader、GroovyBeanDefinitionReader、PropertiesBeanDefinitionReader),並在相應的BeanFactory處指定了具體的實現類(XmlBeanFactory指定使用XmlBeanDefinitionReader)。

  5. 模板方法模式:參見DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions()方法:

    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代理對象,主要用於BeanDefinition的解析與註冊
      BeanDefinitionParserDelegate parent = this.delegate;
      this.delegate = createDelegate(getReaderContext(), root, parent);
    
      //profile屬性處理,只解析滿足當前profile的BeanDefinition
      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);
    
      //實際的BeanDefinition解析與註冊
      parseBeanDefinitions(root, this.delegate);
    
      //解析前置處理,模板方法,留給子類實現
      postProcessXml(root);
    
      this.delegate = parent;
    }
    

    在父類方法中定義了方法的整體流程,並給出了核心實現parseBeanDefinitions()。在覈心邏輯的前後,各定義了一個模板方法,交由子類進行定製化的處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章