前言
在 上一節(jié) spring解密 - 默認(rèn)標(biāo)簽的解析 中,重點(diǎn)分析了 spring 對(duì)默認(rèn)標(biāo)簽是如何解析的,那么本章繼續(xù)講解標(biāo)簽解析,著重講述如何對(duì)自定義標(biāo)簽進(jìn)行解析。話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
自定義標(biāo)簽
在講解 自定義標(biāo)簽解析 之前,先看下如何自定義標(biāo)簽
定義 xsd 文件
定義一個(gè) xsd 文件描述組件內(nèi)容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?xml version= "1.0" encoding= "utf-8" ?> <xsd:schema xmlns= "http://www.battcn.com/schema/battcn" xmlns:xsd= "http://www.w3.org/2001/xmlschema" xmlns:beans= "http://www.springframework.org/schema/beans" targetnamespace= "http://www.battcn.com/schema/battcn" elementformdefault= "qualified" attributeformdefault= "unqualified" > <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= "name" type= "xsd:string" use= "required" /> </xsd:extension> </xsd:complexcontent> </xsd:complextype> </xsd:element> </xsd:schema> |
- 聲明命名空間: 值得注意的是 xmlns 與 targetnamespace 可以是不存在,只要映射到指定 xsd 就行了。
- 定義復(fù)合元素: 這里的 application 就是元素的名稱,使用時(shí) <battcn:application id="battcn"/>
- 定義元素屬性: 元素屬性就是 attribute 標(biāo)簽,我們聲明了一個(gè)必填的 name 的屬性,使用時(shí) <battcn:application id="battcn" name="levin"/>
定義解析規(guī)則
1.創(chuàng)建一個(gè)類實(shí)現(xiàn) beandefinitionparser 接口(也可繼承 spring 提供的類),用來(lái)解析 xsd 文件中的定義和組件定義
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class applicationbeandefinitionparser extends abstractsinglebeandefinitionparser { @override protected class getbeanclass(element element) { // 接收對(duì)象的類型 如:string name = (string) context.getbean("battcn"); return string. class ; } @override protected void doparse(element element, beandefinitionbuilder bean) { // 在 xsd 中定義的 name 屬性 string name = element.getattribute( "name" ); bean.addconstructorargvalue(name); } } |
這里創(chuàng)建了一個(gè) applicationbeandefinitionparser 繼承 abstractsinglebeandefinitionparser(是:beandefinitionparser 的子類), 重點(diǎn)就是重寫的 doparse,在這個(gè)里面解析 xml 標(biāo)簽的,然后將解析出的 value(levin) 通過(guò)構(gòu)造器方式注入進(jìn)去
2.創(chuàng)建一個(gè)類繼承 namespacehandlersupport 抽象類
1
2
3
4
5
6
|
public class battcnnamespacehandler extends namespacehandlersupport { @override public void init() { registerbeandefinitionparser( "application" , new applicationbeandefinitionparser()); } } |
battcnnamespacehandler 的作用特別簡(jiǎn)單,就是告訴 spring 容器,標(biāo)簽 <battcn:application />
應(yīng)該由那個(gè)解析器解析(這里是我們自定義的:applicationbeandefinitionparser),負(fù)責(zé)將組件注冊(cè)到 spring 容器
3.編寫 spring.handlers 和 spring.schemas 文件
文件存放的目錄位于 resources/meta-inf/文件名
spring.handlers
1
|
http\: //www.battcn.com/schema/battcn=com.battcn.handler.battcnnamespacehandler |
spring.schemas
1
|
http\: //www.battcn.com/schema/battcn.xsd=battcn.xsd |
4.使用自定義標(biāo)簽
申明 bean.xml 文件,定義如下
1
2
3
4
5
6
7
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xmlns:battcn= "http://www.battcn.com/schema/battcn" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http: //www.battcn.com/schema/battcn http: //www.battcn.com/schema/battcn.xsd"> <battcn:application id= "battcn" name= "levin" /> </beans> |
創(chuàng)建一個(gè)測(cè)試類,如果看到控制臺(tái)輸出了 levin 字眼,說(shuō)明自定義標(biāo)簽一切正常
1
2
3
4
5
6
7
|
public class application { public static void main(string[] args) { applicationcontext context = new classpathxmlapplicationcontext( "bean.xml" ); string name = (string) context.getbean( "battcn" ); system.out.println(name); } } |
5.如圖所示
源碼分析
自定義標(biāo)簽解析入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class beandefinitionparserdelegate { @nullable public beandefinition parsecustomelement(element ele, @nullable beandefinition containingbd) { // 獲取命名空間地址 http://www.battcn.com/schema/battcn string namespaceuri = getnamespaceuri(ele); if (namespaceuri == null ) { return null ; } // namespacehandler 就是 自定義的 battcnnamespacehandler 中注冊(cè)的 application namespacehandler handler = this .readercontext.getnamespacehandlerresolver().resolve(namespaceuri); if (handler == null ) { error( "unable to locate spring namespacehandler for xml schema namespace [" + namespaceuri + "]" , ele); return null ; } return handler.parse(ele, new parsercontext( this .readercontext, this , containingbd)); } } |
與默認(rèn)標(biāo)簽解析規(guī)則一樣的是,都是通過(guò) getnamespaceuri(node node)
來(lái)獲取命名空間,那么 this.readercontext.getnamespacehandlerresolver()
是從哪里獲取的呢?我們跟蹤下代碼,可以發(fā)現(xiàn)在項(xiàng)目啟動(dòng)的時(shí)候,會(huì)在 xmlbeandefinitionreader 將所有的 meta-inf/spring.handles 文件內(nèi)容解析,存儲(chǔ)在 handlermappers(一個(gè)concurrenthashmap) 中,在調(diào)用 resolve(namespaceuri)
校驗(yàn)的時(shí)候在將緩存的內(nèi)容提取出來(lái)做對(duì)比
1
2
3
4
5
6
7
8
|
public class xmlbeandefinitionreader { public namespacehandlerresolver getnamespacehandlerresolver() { if ( this .namespacehandlerresolver == null ) { this .namespacehandlerresolver = createdefaultnamespacehandlerresolver(); } return this .namespacehandlerresolver; } } |
resolve
1.加載指定的 namespacehandler 映射,并且提取的 namespacehandler 緩存起來(lái),然后返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public class defaultnamespacehandlerresolver { @override @nullable public namespacehandler resolve(string namespaceuri) { map<string, object> handlermappings = gethandlermappings(); // 從 handlermappings 提取 handlerorclassname object handlerorclassname = handlermappings.get(namespaceuri); if (handlerorclassname == null ) { return null ; } else if (handlerorclassname instanceof namespacehandler) { return (namespacehandler) handlerorclassname; } else { string classname = (string) handlerorclassname; try { class <?> handlerclass = classutils.forname(classname, this .classloader); if (!namespacehandler. class .isassignablefrom(handlerclass)) { throw new fatalbeanexception( "class [" + classname + "] for namespace [" + namespaceuri + "] does not implement the [" + namespacehandler. class .getname() + "] interface" ); } // 根據(jù)命名空間尋找對(duì)應(yīng)的信息 namespacehandler namespacehandler = (namespacehandler) beanutils.instantiateclass(handlerclass); // handler 初始化 namespacehandler.init(); handlermappings.put(namespaceuri, namespacehandler); return namespacehandler; } catch (classnotfoundexception ex) { throw new fatalbeanexception( "namespacehandler class [" + classname + "] for namespace [" + namespaceuri + "] not found" , ex); } catch (linkageerror err) { throw new fatalbeanexception( "invalid namespacehandler class [" + classname + "] for namespace [" + namespaceuri + "]: problem with handler class file or dependent class" , err); } } } } |
標(biāo)簽解析
加載完 namespacehandler 之后,battcnnamespacehandler 就已經(jīng)被初始化為 了,而 battcnnamespacehandler 也調(diào)用了 init()
方法完成了初始化的工作。因此就接著執(zhí)行這句代碼: handler.parse(ele, new parsercontext(this.readercontext, this, containingbd));
具體標(biāo)簽解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class namespacehandlersupport { @override @nullable public beandefinition parse(element element, parsercontext parsercontext) { beandefinitionparser parser = findparserforelement(element, parsercontext); return (parser != null ? parser.parse(element, parsercontext) : null ); } @nullable private beandefinitionparser findparserforelement(element element, parsercontext parsercontext) { // 解析出 <battcn:application /> 中的 application string localname = parsercontext.getdelegate().getlocalname(element); beandefinitionparser parser = this .parsers.get(localname); if (parser == null ) { parsercontext.getreadercontext().fatal( "cannot locate beandefinitionparser for element [" + localname + "]" , element); } return parser; } } |
簡(jiǎn)單來(lái)說(shuō)就是從 parsers 中尋找到 applicationbeandefinitionparser 實(shí)例,并調(diào)用其自身的 doparse 方法進(jìn)行進(jìn)一步解析。最后就跟解析默認(rèn)標(biāo)簽的套路一樣了…
總結(jié)
熬過(guò)幾個(gè)無(wú)人知曉的秋冬春夏,撐過(guò)去一切都會(huì)順著你想要的方向走…
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
說(shuō)點(diǎn)什么
全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/chapter2
原文鏈接:http://blog.battcn.com/2018/01/12/spring/spring-3/