關(guān)于Spring自定義XML schema 擴(kuò)展的問題(Spring面試高頻題)
引言
自從SpringBoot
時(shí)代的到來,去除了Spring
的各種繁瑣的XML
配置,讓我們可以騰出雙手以便于更加專注的搬磚。記得那時(shí)候剛學(xué)Spring
的時(shí)候,每天被Spring
的各種XMl
配置文件折磨的不行,每引入一個(gè)新的框架,最擔(dān)心的就是jar
沖突、哪個(gè)配置文件又配的不對(duì)、配置文件沒有起作用。所以每次搭建好一個(gè)項(xiàng)目就把配置文件用小筆記記錄下來, 方便下次在整合項(xiàng)目的時(shí)候直接copy復(fù)制就好。下面我們就以Spring
整合dubbo
的事例看下
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demo-provider"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo" port="20890"/> <bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/> <dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/> </beans>
上述代碼中我們有看到dubbo
自定義了一套自己的標(biāo)簽,dubbo:application ,dubbo:registry ,dubbo:protocol,dubbo:service我們心中是不是有點(diǎn)小疑問:這些標(biāo)簽在Spring
項(xiàng)目啟動(dòng)的時(shí)候是如何被Spring
管理的?是怎樣被Spring
來識(shí)別的?如果我們自己隨便定義一個(gè)標(biāo)簽Spring
是否能夠識(shí)別?我們?nèi)シ?code>Spring的官網(wǎng)發(fā)現(xiàn)這玩意其實(shí)就是Spring
提供的 XML schema
的擴(kuò)展支持。只要按照它的步驟來,我們就可以配置任何我們自定義的標(biāo)簽。XML schema
擴(kuò)展機(jī)制是什么?這個(gè)也許好多人沒聽過:
★Spring 為基于 XML 構(gòu)建的應(yīng)用提供了一種擴(kuò)展機(jī)制,用于定義和配置 Bean。它允許使用者編寫自定義的 XML bean 解析器,并將解析器本身以及最終定義的 Bean 集成到 Spring IOC 容器中。
”
我們可以看看官網(wǎng)https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#xml-custom 10.2. XML Schema Authoring 這個(gè)是主要介紹它的。
如何實(shí)現(xiàn)一個(gè)自定義 XML 擴(kuò)展
官網(wǎng)有介紹,要實(shí)現(xiàn)一個(gè)自定義的XML Schema
總共需要4步:
★編寫一個(gè) XML schema 文件描述的你節(jié)點(diǎn)元素。
編寫一個(gè) NamespaceHandler 的實(shí)現(xiàn)類
編寫一個(gè)或者多個(gè) BeanDefinitionParser 的實(shí)現(xiàn) (關(guān)鍵步驟).
注冊(cè)上述的 schema 和 handler。
”
既然只要按照這四步來,那我們就照著這個(gè)文檔來自己實(shí)現(xiàn)一個(gè)。
Authoring the Schema
編寫一個(gè)javajr.xsd 放入項(xiàng)目的resources/META-INF文件夾里面(這個(gè)也可以是其他路徑)
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool" xmlns="https://www.javajr.cn/schema/javajr" targetNamespace="https://www.javajr.cn/schema/javajr"> <xsd:import namespace="http://www.springframework.org/schema/beans"/> <xsd:element name="application"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:attribute name="website" type="xsd:string" use="required"/> <xsd:attribute name="weixin" type="xsd:string" use="required"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element> </xsd:schema>
targetNamespace="https://www.javajr.cn/schema/javajr"
- 這里
targetNamespace
的地址后面有用到。
這里我們就定義了一個(gè)元素application 里面有兩個(gè)屬性分別為website
和weixin
。
編寫一個(gè) NamespaceHandler
package org.spring.demo.schema; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class MyNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { // 這個(gè)名字也不是隨便取的,上面編寫xsd的根節(jié)點(diǎn)元素的name, <xsd:element name="application"> registerBeanDefinitionParser("application", new MyBeanDefinitionParser()); } }
這個(gè)NamespaceHandler
就是將一個(gè) XML
節(jié)點(diǎn)解析成 IOC
容器中的一個(gè)實(shí)體類。也就是說相當(dāng)于在xml
里面的配置的對(duì)象,通過Spring ioc
容器管理起來了
編寫 BeanDefinitionParser 的實(shí)現(xiàn)類
package org.spring.demo.schema; import org.spring.demo.domain.JavajrDomain; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; public class MyBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return JavajrDomain.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder bean) { // this however is an optional property String website = element.getAttribute("website"); if (StringUtils.hasText(website)) { bean.addPropertyValue("website",website); } String weiXin = element.getAttribute("weixin"); if (StringUtils.hasText(weiXin)) { bean.addPropertyValue("weixin",weiXin); } } }
上面在這個(gè)實(shí)現(xiàn)類只是簡單的做了一個(gè)賦值操作,你如果需要有自己的邏輯業(yè)務(wù)也可以自行來實(shí)現(xiàn)。上面還有一個(gè)JavajrDomain這個(gè)實(shí)體類就不貼代碼,就一個(gè)簡單的javabean
里面包含了兩個(gè)屬性weixin和website。
注冊(cè)schema組件
最后在resources/META-INF
目錄下添加兩個(gè)配置文件(spring.handler和spring.schema
):
resources/META-INF/spring.handlers
https\://www.javajr.cn/schema/javajr=org.spring.demo.schema.MyNamespaceHandler
resources/META-IN/spring.schemas
https\://www.javajr.cn/schema/javajr.xsd=META-INF/javajr.xsd
在這個(gè)地方的時(shí)候我們其實(shí)可以以版本號(hào)來進(jìn)行命名,方便我們可以使用多個(gè)不同的版本,Spring-beans 就是這么玩的。
測(cè)試自定義schema
在resources
目錄下新建一個(gè)applicationContext.xml
文件
這個(gè)文件就是使用下我們我們自己自定義的schema
,這個(gè)文件需要注意的就是上面標(biāo)紅的這幾行,一般如果我們有引入過第三方的框架,比如mq
、或者dubbo
等它們都有自定義的這些玩意。
編寫一個(gè)啟動(dòng)類
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); JavajrDomain bean = ctx.getBean(JavajrDomain.class); System.out.println(bean.toString()); }
我們可以看到控制臺(tái)輸出
JavajrDomain{weixin='javajr8', website='javajr.cn'}
到這里我們自己實(shí)現(xiàn)的一個(gè) XML schema
就完成了,是不是很簡單,只要照著官方文檔擼就可以了。照著擼的過程可能有幾個(gè)小細(xì)節(jié)需要注意下引入 XML schema
的時(shí)候需要注意下空格,或者一些特殊符號(hào)。上述代碼已經(jīng)提交到了gitee上https://gitee.com/javajr/spring-schema-demo
感興趣的朋友可以直接下載下來run
下,不過還是不建議這么玩,最好還是自己動(dòng)手去嘗試下,畢竟也就四步,照著文檔來。
Dubbo 中的 XML schema 擴(kuò)展
在文章開始的時(shí)候我們有介紹dubbo
自定義的XML schema
,下面我們一起打開dubbo
源碼看看它是如何來實(shí)現(xiàn)的,看下面這個(gè)截圖,也是按照那四步來的。
SpringBoot的starter
現(xiàn)在有了SpringBoot
之后以前用這個(gè) XML schema
配置的框架,大多數(shù)都會(huì)有對(duì)應(yīng)的starter
來進(jìn)行封裝,starter
的使用比起 XML schema
的使用還是簡單多了,開箱即用,無需編寫很多的配置文件。如果不是很清楚SpringBoot
的starter
的推薦去看看這兩篇文章《面試高頻題:springBoot自動(dòng)裝配的原理你能說出來嗎?》《保姆級(jí)教程,手把手教你實(shí)現(xiàn)一個(gè)SpringBoot的starter》。
總結(jié)
雖然現(xiàn)在XML schema
擴(kuò)展用的不多了,但是應(yīng)該也還有比較老的項(xiàng)目在使用吧,如果還是比較老的項(xiàng)目,需要引入一個(gè)什么樣的框架,我們至少需要知道需要怎么去引入,網(wǎng)上雖然有很多文章可以借鑒,但是我們也應(yīng)該知其然知其所以然。而不是直接把配置文件單純的copy
過來。我們應(yīng)該知道為啥需要copy
這個(gè)xsd
,為什么沒有這個(gè)xsd
,idea
不糊識(shí)別會(huì)報(bào)錯(cuò)。
以上就是關(guān)于Spring自定義XML schema 擴(kuò)展的問題(Spring面試高頻題)的詳細(xì)內(nèi)容,更多關(guān)于Spring XML schema 擴(kuò)展的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IDEA運(yùn)行SpringBoot項(xiàng)目的圖文教程
本文主要介紹了IDEA運(yùn)行SpringBoot項(xiàng)目的圖文教程,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05Java面向?qū)ο蠡A(chǔ)知識(shí)之?dāng)?shù)組和鏈表
這篇文章主要介紹了Java面向?qū)ο蟮闹當(dāng)?shù)組和鏈表,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下2021-11-11SpringMvc直接接收json數(shù)據(jù)自動(dòng)轉(zhuǎn)化為Map的實(shí)例
今天小編就為大家分享一篇SpringMvc直接接收json數(shù)據(jù)自動(dòng)轉(zhuǎn)化為Map的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08Java基于IDEA實(shí)現(xiàn)http編程的示例代碼
這篇文章主要介紹了Java基于IDEA實(shí)現(xiàn)http編程的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04