07.Spring Bean 載入 - BeanDefinitionReader

來源:互聯網
上載者:User

標籤:name   blog   jsb   interface   通過   封裝   屬性   parse   2.3   

基本概念

BeanDefinitionReader ,該介面的作用就是載入 Bean。

在 Spring 中,Bean 一般來說都在設定檔中定義。而在配置的路徑由在 web.xml 中定義。所以載入 Bean 的步驟大致就是:

  • 載入資源,通過設定檔的路徑(Location)載入設定檔(Resource)

  • 解析資源,通過解析設定檔的內容得到 Bean。

下面來看它的介面定義:

public interface BeanDefinitionReader {    BeanDefinitionRegistry getRegistry();    ResourceLoader getResourceLoader();    ClassLoader getBeanClassLoader();    BeanNameGenerator getBeanNameGenerator();    // 通過 Resource 載入 Bean int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; // 通過 location 載入資源 int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;}

具體的繼承關係如下:

流程分析

首先來看 Spring Ioc 容器從啟動開始到調用 BeanDefinitionReader 載入 Bean 的過程如下:

注意:由於這裡採用 XML 檔案作為 Spring 的設定檔,所以預設調用 XmlBeanDefinitionReader 來處理。

1.通過 BeanFactory 載入 Bean

在 Spring 容器(ApplicationContext)內部存在一個內部容器(BeanFactory)負責 Bean 的建立與管理。

在建立完 BeanFactory ,下一步就是要去載入 Bean。它由 loadBeanDefinitions 方法負責。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)    throws BeansException, IOException {    // 1.建立 BeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 2.設定 BeanDefinitionReader 的相關屬性 // 2.1.設定 Environment,即環境,與容器的環境一致 beanDefinitionReader.setEnvironment(getEnvironment()); // 2.2.設定 ResourceLoader,即資源載入器,因為容器實現了該介面,具體載入資源的功能 beanDefinitionReader.setResourceLoader(this); // 2.3.設定 EntityResolver,即實體解析器,這裡用於解析資源載入器載入的資源內容 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 3.初始化 BeanDefinitionReader ,空方法 initBeanDefinitionReader(beanDefinitionReader); // 4.通過 BeanDefinitionReader 載入 Bean loadBeanDefinitions(beanDefinitionReader);}// 建構函式public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { // 內部 BeanFactory 被當作 Bean 註冊器 super(registry);}

觀察代碼,該方法的主要目的是建立了 BeanDefinitionReader ,並由它去載入 Bean。具體過程如下:

  • 建立 BeanDefinitionReader
  • 設定 BeanDefinitionReader 的相關屬性
  • 初始化 BeanDefinitionReader
  • 通過 BeanDefinitionReader 載入 Bean
2.通過 BeanDefinitionReader 載入 Bean

在建立完 BeanDefinitionReader 後,則就需要通過它來 載入 Bean,過程如下:

// 通過 BeanDefinitionReader 載入 Bean protected void loadBeanDefinitions(XmlBeanDefinitionReader reader)     throws IOException { // 取得 Spring 容器的所有設定檔 String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { // 調用 BeanDefinitionReader 載入 Bean reader.loadBeanDefinitions(configLocation); } }}
3.通過 Location 載入 Bean

載入的 Bean 的責任被交給了 BeanDefinitionReader ,下面來看看該類的 loadBeanDefinitions 方法。

public int loadBeanDefinitions(String location)     throws BeanDefinitionStoreException {    return loadBeanDefinitions(location, null);}public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { // 1.取得資源載入器,即容器本身 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { // 拋出異常... } // 判斷資源載入器類型 if (resourceLoader instanceof ResourcePatternResolver) { // 說明該 ResourceLoader 可以基於路徑載入多個資源 try { // 2.載入資源 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 3.通過 Resource 載入 Bean int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } // 省略代碼... return loadCount; }catch (IOException ex) { // 拋出異常... } }else { // 表示 ResourceLoader 只能載入一個資源 Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } // 省略代碼... return loadCount; }}

觀察代碼,該方法的工作流程可分為四個步驟:

  • 取得資源載入器

  • 載入資源,通過 location 利用 ResourceLoader 載入

  • 通過 Resource 載入 Bean

4.通過 Resource 載入 Bean

在得到資源(Resource)後,該方法對它進行了封裝,使其變成一個 EncodedResource 對象。

public int loadBeanDefinitions(Resource resource)     throws BeanDefinitionStoreException {    // EncodedResource 表示編碼類別型的資源 return loadBeanDefinitions(new EncodedResource(resource));}// 建構函式public EncodedResource(Resource resource, String encoding) { // 封裝資源,並帶上編碼類別型 this(resource, encoding, null);}
5.通過 EncodedResource 載入 Bean
public int loadBeanDefinitions(EncodedResource encodedResource)     throws BeanDefinitionStoreException {    // 省略代碼... // 1.取得[已載入的資源]的集合,用於存在已載入的資源。空,則建立。 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } // 2.將當前資源加入集合 if (!currentResources.add(encodedResource)) { // 拋出異常... } try { // 3.將資源轉換成流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //5.通過流建立 InputSource ,並設定編碼 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //6.通過 InputSource 載入 Bean return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); }finally { inputStream.close(); } }catch (IOException ex) { // 拋出異常... }finally { // 移除已完成解析的資源 currentResources.remove(encodedResource); // 集合為空白,則一併刪除 if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } }}
  • 取得已載入的資源集合
  • 將當前資源添加到集合
  • 將資源轉換成流
  • 通過流建立 InputSource ,並設定編碼
  • 通過 InputSource 載入 Bean
6.通過 InputSource 載入 Bean

之所以把 Resource 轉換成 InputSource ,就是為了 SAX 解析作準備。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)     throws BeanDefinitionStoreException {    try { // 1.解析 XML 檔案 Document doc = doLoadDocument(inputSource, resource); // 2.註冊 Bean return registerBeanDefinitions(doc, resource); }catch (BeanDefinitionStoreException ex) { // 拋出異常... }catch (SAXParseException ex) { // 拋出異常... }catch (SAXException ex) { // 拋出異常... }catch (ParserConfigurationException ex) { // 拋出異常... }catch (IOException ex) { // 拋出異常... }catch (Throwable ex) { // 拋出異常... }}// 解析 XML 檔案,並返回 Document 對象。protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());}
3.Bean 註冊

上一步中 XmlBeanDefinitionReader 已經取得了 XML 的 Document 對象,完成了資源的解析過程。

下一步就是 Bean 的註冊過程。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {    // 利用 documentReader 對設定檔的內容進行解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 取得已經註冊 BeanDefinition int countBefore = getRegistry().getBeanDefinitionCount(); // 關鍵 -> 註冊 BeanDefinition (包含解析過程) documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;}
總結

分析完 BeanDefinitionReader 具體工作流程,最後通過一個圖簡單闡述:

07.Spring Bean 載入 - BeanDefinitionReader

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.