基于Spring開(kāi)發(fā)之自定義標(biāo)簽及其解析
Spring框架是現(xiàn)在Java最流行的開(kāi)源框架之一,并且Spring下的各種子項(xiàng)目對(duì)某些特定問(wèn)題的解決有很好的支持。因此,如果能在Spring 基礎(chǔ)上實(shí)現(xiàn)搭建自己的一套框架(基于XML配置)。就必然需要實(shí)現(xiàn)一些自定義的標(biāo)簽,主要是方便使用我們框架的人能夠快速、簡(jiǎn)單進(jìn)行配置。
1. XML Schema
要想自定義標(biāo)簽,首先第一步需要寫(xiě)自己的XML Schema。XML Schema的個(gè)人感覺(jué)比較復(fù)雜,網(wǎng)上的教程比較簡(jiǎn)單,因此可以參照spring-beans.xsd依葫蘆畫(huà)瓢。這里就按照我自己的理解進(jìn)行簡(jiǎn)單介紹一下吧。
1.1 最簡(jiǎn)單的標(biāo)簽
一個(gè)最簡(jiǎn)單的標(biāo)簽,形式如:
<bf:head-routing key="1" value="1" to="test2"/>
該標(biāo)簽只包含了若干屬性,我們就在xsd文件中這么定義
<!-- 聲明一個(gè)標(biāo)簽,名字為head-routing,他的類型為headRouting-->
<xsd:element name="head-routing" type="headRouting"></xsd:element>
<!-- 定義head-routing的類型,這里定義它有key,value,to,patten四個(gè)屬性 -->
<xsd:complexType name="headRouting">
<xsd:attribute name="key" type="xsd:string" use="required"></xsd:attribute>
<xsd:attribute name="value" type="xsd:string" use="required"></xsd:attribute>
<xsd:attribute name="to" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="patten" type="xsd:string" default="string"></xsd:attribute>
</xsd:complexType>
在<xsd:attribute>標(biāo)簽中的type是用來(lái)定義該屬性的格式,例如
- xsd:string 表示是一個(gè)字符串,對(duì)格式?jīng)]什么要求
- xsd:id 表示該屬性的值是一個(gè)id,有格式要求(例如不能以數(shù)字開(kāi)頭)。
- xsd:IDREF 表示該屬性的值與某xsd:id屬性的值對(duì)應(yīng)
- 其他還有很多,例如number,double,datetime等等。
1.2 復(fù)雜點(diǎn)的標(biāo)簽
所謂復(fù)雜,其實(shí)就是嵌套的標(biāo)簽,形式如:
<bf:stop id="test1" ref="testNode">
<bf:head-routing key="1" value="1" to="test2"/>
</bf:stop>
其實(shí)只要參照Spring 中<bean>標(biāo)簽的xsd依葫蘆畫(huà)瓢,首先是定義stop標(biāo)簽
<xsd:element name="stop">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:group ref="stopElements"/>
<xsd:attributeGroup ref="stopAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
其中,
<xsd:extension base="beans:identifiedType"> 定義了該標(biāo)簽的id屬性,注意這里引用的是spring-beans中的type,
<xsd:group ref="stopElements"/>中定義了<bf:stop>標(biāo)簽允許的子標(biāo)簽
<xsd:attributeGroup ref="stopAttributes"/> 定義了<bf:stop>標(biāo)簽允許的屬性
<xsd:group name="stopElements">
<xsd:sequence>
<xsd:element ref="description" minOccurs="0"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="head-routing"/>
<!-- 有更多的子標(biāo)簽繼續(xù)在這里添加,例如<xsd:element ref="properties"/> -->
</xsd:choice>
</xsd:sequence>
</xsd:group>
<xsd:attributeGroup name="stopAttributes">
<xsd:attribute name="ref" type="xsd:IDREF" use="required">
<xsd:annotation>
<xsd:appinfo>
<!-- 這里是使用了Spring tool xsd中的標(biāo)簽,格式校驗(yàn)-->
<tool:annotation kind="ref">
<tool:expected-type type="com.lizo.node.Station"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<!-- 有更多的子標(biāo)簽繼續(xù)在這里添加,例如<xsd:attribute name="value" type="xsd:string"/> -->
2. 配置文件
完成了xsd文件編寫(xiě)后,還需要讓該文件生效,就需要在項(xiàng)目的resource/META-INF包里面配置2個(gè)文件spring.handlers和spring.schemas
2.1 spring.schemas
改配置文件主要是用一個(gè)url來(lái)映射我們第一步配置好的文件,形式如下
http\://www.lizo.com/schema/bf.xsd=META-INF/bf.xsd
這樣,就可以在Spring的xml配置文件中加入spring.schemas的url,省略掉其他的,在<beans>標(biāo)簽中增加如下信息
<beans
..
xmlns:bf="http://www.lizo.com/schema/bf"
xsi:schemaLocation="
...
http://www.lizo.com/schema/bf
http://www.lizo.com/schema/bf.xsd
">
完成這步以后,就可以在xml中寫(xiě)自己的標(biāo)簽了,例如自定義標(biāo)簽的namespace為bf,
<bf:stop id="test123" ref="testNode">
<bf:head-routing key="1" value="1" to="test1"/>
<bf:head-routing key="3" value="4" to="test2"/>
</bf:stop>
2.2 spring.handlers
這個(gè)配置文件用來(lái)配置解析我們bf標(biāo)簽,然后生成一些BeanDefinition進(jìn)行注冊(cè)。例如
http\://www.lizo.com/schema/bf=com.lizo.config.BusinessFlowNamespaceHandlerSupport
其中 BusinessFlowNamespaceHandlerSupport就是我們用來(lái)解析標(biāo)簽
3. 自定義標(biāo)簽解析
在上一步中,我們配置了com.lizo.config.BusinessFlowNamespaceHandlerSupport類作為解析自定義標(biāo)簽的類,所以namespace為bf的標(biāo)簽,都會(huì)用這里注冊(cè)的標(biāo)簽解析器來(lái)解析
public class BusinessFlowNamespaceHandlerSupport extends NamespaceHandlerSupport {
public void init() {
//注冊(cè)用于解析<bf:stop>的解析器
registerBeanDefinitionParser("stop", new BusinessFlowBeanDefinitionParser());
}
}
我們自定義的標(biāo)簽解析器BusinessFlowBeanDefinitionParser是要實(shí)現(xiàn)BeanDefinitionParser 接口的
public interface BeanDefinitionParser {
BeanDefinition parse(Element element, ParserContext parserContext);
}
一般來(lái)說(shuō),注冊(cè)bean的基本流程為:
- 解析標(biāo)簽
- 根據(jù)解析的值生成BeanDefinition,
- 注冊(cè)標(biāo)簽
解析標(biāo)簽就不用說(shuō),重點(diǎn)說(shuō)說(shuō)怎么生成BeanDefinition
3.1 生成BeanDefinition
一個(gè)最簡(jiǎn)單的BeanDefinition通過(guò)設(shè)置Class和屬性的注入就可以完成。如下:
RootBeanDefinition nodeWrapDefinition = new RootBeanDefinition();
//該BeanDefinition對(duì)應(yīng)的是什么類
nodeWrapDefinition.setBeanClass(StationRoutingWrap.class);
//name是解析標(biāo)簽后獲得的值
nodeWrapDefinition.getPropertyValues().addPropertyValue("name", name);
RuntimeBeanReference
RuntimeBeanReference 用于在運(yùn)行時(shí)去獲取BeanDefinition,因?yàn)樵谖覀儎?chuàng)建這個(gè)BeanDefinition的時(shí)候我們只知道他的beanName,并不確定是否已經(jīng)注冊(cè)了,這個(gè)時(shí)候就需要用RuntimeBeanReference,例如
RuntimeBeanReference refBean = new RuntimeBeanReference(ref);
nodeWrapDefinition.getPropertyValues().addPropertyValue("station", refBean);
集合類BeanDefinition
某個(gè)BeanDefinition注入的屬性為一個(gè)List,這個(gè)時(shí)候就需要用ManagedList(同理有ManagedMap,ManagedSet),
ManagedList routingConditions = new ManagedList();
....
nodeWrapDefinition.getPropertyValues().add("routing", routing);
3.2 注冊(cè)bean
注冊(cè)BeanDefinitionParser 接口的函數(shù)中有個(gè)參數(shù)ParserContext,有個(gè)方法為getRegistry(),因此,注冊(cè)bean的時(shí)候就很簡(jiǎn)單了
parserContext.getRegistry().registerBeanDefinition("beanName",nodeWrapDefinition);
總結(jié)
通過(guò)以上三步,就可以實(shí)現(xiàn)自己定義標(biāo)簽,并且在Spring容器中注入相關(guān)的bean。讓我們的框架使用起來(lái)更方便
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaWeb項(xiàng)目FullCalendar日歷插件使用的示例代碼
本篇文章主要介紹了JavaWeb項(xiàng)目FullCalendar日歷插件使用的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
IDEA在一個(gè)工作空間中管理多個(gè)項(xiàng)目的詳細(xì)步驟
這篇文章主要介紹了IDEA在一個(gè)工作空間中管理多個(gè)項(xiàng)目的詳細(xì)步驟,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
JavaWeb?Listener?利用Session統(tǒng)計(jì)在線人數(shù)
這篇文章主要為大家介紹了JavaWeb?Listener?利用Session統(tǒng)計(jì)在線人數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Java中的讀寫(xiě)鎖ReentrantReadWriteLock源碼分析
這篇文章主要介紹了Java中的讀寫(xiě)鎖ReentrantReadWriteLock源碼分析,ReentrantReadWriteLock 分為讀鎖和寫(xiě)鎖兩個(gè)實(shí)例,讀鎖是共享鎖,可被多個(gè)線程同時(shí)使用,寫(xiě)鎖是獨(dú)占鎖,持有寫(xiě)鎖的線程可以繼續(xù)獲取讀鎖,反之不行,需要的朋友可以參考下2023-12-12
三分鐘帶你了解SpringBoot真正的啟動(dòng)引導(dǎo)類
這篇文章主要介紹了三分鐘帶你了解SpringBoot真正的啟動(dòng)引導(dǎo)類,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Seata集成Mybatis-Plus解決多數(shù)據(jù)源事務(wù)問(wèn)題
當(dāng)進(jìn)行業(yè)務(wù)操作時(shí),訂單發(fā)生異常 ,進(jìn)行了回滾操作,因?yàn)樵诓煌臄?shù)據(jù)庫(kù)實(shí)例中,余額卻扣除成功,此時(shí)發(fā)現(xiàn)數(shù)據(jù)不一致問(wèn)題,本文給大家介紹Seata集成Mybatis-Plus解決多數(shù)據(jù)源事務(wù)問(wèn)題,感興趣的朋友一起看看吧2023-11-11
深入理解java動(dòng)態(tài)代理的兩種實(shí)現(xiàn)方式(JDK/Cglib)
本篇文章主要介紹了java動(dòng)態(tài)代理的兩種實(shí)現(xiàn)方式,詳細(xì)的介紹了JDK和Cglib的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-04-04

