「每日分享」Spring中的XML schema擴展機制

您的關注、點贊、轉發是對我們最大的支持

前言

很久沒有寫關於 Spring 的文章了,最近在系統梳理 Dubbo 代碼的過程中發現了 XML schema 這個被遺漏的知識點。由於工作中使用 SpringBoot 比較多的原因,幾乎很少接觸 XML,此文可以算做是亡羊補牢,另一方面,也為後續的 Dubbo 源碼解析做個鋪墊。

XML schema 擴展機制是啥?這並不是一塊很大的知識點,翻閱一下 Spring 的文檔,我甚至沒找到一個貫穿上下文的詞來描述這個功能, XMLSchemaAuthoring 是文檔中對應的標題,簡單來說:

Spring 為基於 XML 構建的應用提供了一種擴展機制,用於定義和配置 Bean。 它允許使用者編寫自定義的 XML bean 解析器,並將解析器本身以及最終定義的 Bean 集成到 Spring IOC 容器中。


「每日分享」Spring中的XML schema擴展機制


Dubbo 依賴了 Spring,並提供了一套自定義的 XML 標籤, , , , 。作為使用者,大多數人只需要關心這些參數如何配置,但不知道有沒有人好奇過,它們是如何加載進入 Spring 的 IOC 容器中被其他組件使用的呢?這便牽扯出了今天的主題:Spring 對 XML schema 的擴展支持。

自定義 XML 擴展

為了搞懂 Spring 的 XML 擴展機制,最直接的方式便是實現一個自定義的擴展。實現的步驟也非常簡單,分為四步:

  1. 編寫一個 XML schema 文件描述的你節點元素。
  2. 編寫一個 NamespaceHandler 的實現類
  3. 編寫一個或者多個 BeanDefinitionParser 的實現 (關鍵步驟).
  4. 註冊上述的 schema 和 handler。

我們的目的便是想要實現一個 kirito XML schema,我們的項目中可以自定義 kirito.xml,在其中會以 kirito 為標籤來定義不同的類,並在最終的測試代碼中驗證這些聲明在 kirito.xml 的類是否被 Spring 成功加載。大概像這樣,是不是和 dubbo.xml 的格式很像呢?

「每日分享」Spring中的XML schema擴展機制


動手實現

有了明確的目標,我們逐步開展工作。

1 編寫kirito.xsd

resources/META-INF/kirito.xsd

「每日分享」Spring中的XML schema擴展機制

① 注意這裡的 targetNamespace="http://www.cnkirito.moe/schema/kirito" 這便是之後 kirito 標籤的關鍵點。

② kirito.xsd 定義了兩個元素: application 和 service,出於簡單考慮,都只有一個 name 字段。

schema 的意義在於它可以和 eclipse/IDEA 這樣智能化的集成開發環境形成很好的搭配,在編輯 XML 的過程中,用戶可以獲得告警和提示。 如果配置得當,可以使用自動完成功能讓用戶在事先定義好的枚舉類型中進行選擇。

2 編寫KiritoNamespaceHandler

「每日分享」Spring中的XML schema擴展機制

完成 schema 之後,還需要一個 NamespaceHandler 來幫助 Spring 解析 XML 中不同命名空間的各類元素。




不同的命名空間需要不同的 NamespaceHandler 來處理,在今天的示例中,我們使用 KiritoNamespaceHandler 來解析 kirito 命名空間。KiritoNamespaceHandler 繼承自 NamespaceHandlerSupport 類,並在其 init() 方法中註冊了兩個 BeanDefinitionParser ,用於解析 kirito 命名空間/kirito.xsd 約束中定義的兩個元素:application,service。BeanDefinitionParser 是下一步的主角,我們暫且跳過,將重心放在父類 NamespaceHandlerSupport 之上。

「每日分享」Spring中的XML schema擴展機制

NamespaceHandlerSupport 是 NamespaceHandler 命名空間處理器的抽象實現,我粗略看了NamespaceHandler 的幾個實現類,parse 和 decorate 方法可以完成元素節點的組裝並通過 ParserContext 註冊到 Ioc 容器中,但實際我們並沒有調用這兩個方法,而是通過 init() 方法註冊 BeanDefinitionParser 來完成解析節點以及註冊 Bean 的工作,所以對於 NamespaceHandler,我們主要關心 init 中註冊的兩個 BeanDefinitionParser 即可。

3 編寫KiritoBeanDefinitionParser

在文章開始我們便標記到 BeanDefinitionParser 是最為關鍵的一環,每一個 BeanDefinitionParser 實現類都負責一個映射,將一個 XML 節點解析成 IOC 容器中的一個實體類。

「每日分享」Spring中的XML schema擴展機制

由於我們的實體類是非常簡單的,所以不存在很複雜的解析代碼,而實際項目中,往往需要大量的解析步驟。parse 方法會解析一個個 XML 中的元素,使用 RootBeanDefinition 組裝成對象,並最終通過 parserContext 註冊到 IOC 容器中。

至此,我們便完成了 XML 文件中定義的對象到 IOC 容器的映射。

4 註冊schema和handler

最後一步還需要通知 Spring,告知其自定義 schema 的所在之處以及對應的處理器。

resources/META-INF/spring.handlers

http\:
//www.cnkirito.moe/schema/kirito=moe.cnkirito.sample.xsd.KiritoNamespaceHandler

resources/META-INF/spring.schemas

http\:
//www.cnkirito.moe/schema/kirito/kirito.xsd=META-INF/kirito.xsd

沒有太多可以說的,需要遵守 Spring 的約定。

至此一個自定義的 XML schema 便擴展完成了,隨後來驗證一下。

驗證擴展

我們首先定義好 kirito.xml

「每日分享」Spring中的XML schema擴展機制

使用 Spring 去加載它,並驗證 IOC 容器中是否存在註冊成功的 Bean。

「每日分享」Spring中的XML schema擴展機制

觀察控制檯的輸出:

kirito-demo-service kirito-demo-application

一個基礎的基於 XML schema 的擴展便完成了。

Dubbo中的XML schema擴展

最後我們以 Dubbo 為例,看看一個成熟的 XML schema 擴展是如何被應用的。

「每日分享」Spring中的XML schema擴展機制


剛好對應了四個標準的擴展步驟,是不是對 XML 配置下的 Dubbo 應用有了更好的理解了呢?

順帶一提,僅僅完成 Bean 的註冊還是不夠的,在“註冊”的同時,Dubbo 還進行了一系列其他操作如:暴露端口,開啟服務器,完成註冊中心的註冊,生成代理對象等等行為,由於不在本文的範圍內,後續的 Dubbo 專題會專門介紹這些細節,本文便是瞭解 Dubbo 加載流程的前置文章了。


分享到:


相關文章: