您好,登錄后才能下訂單哦!
小編給大家分享一下Spring源碼解密之自定義標(biāo)簽的示例分析,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
自定義標(biāo)簽
在講解 自定義標(biāo)簽解析 之前,先看下如何自定義標(biāo)簽
定義 XSD 文件
定義一個(gè) XSD 文件描述組件內(nèi)容
<?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 提供的類),用來解析 XSD 文件中的定義和組件定義
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) 通過構(gòu)造器方式注入進(jìn)去
2.創(chuàng)建一個(gè)類繼承 NamespaceHandlerSupport 抽象類
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
http\://www.battcn.com/schema/battcn=com.battcn.handler.BattcnNamespaceHandler
spring.schemas
http\://www.battcn.com/schema/battcn.xsd=battcn.xsd
4.使用自定義標(biāo)簽
申明 bean.xml 文件,定義如下
<?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 字眼,說明自定義標(biāo)簽一切正常
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)簽解析入口
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ī)則一樣的是,都是通過 getNamespaceURI(Node node)
來獲取命名空間,那么 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)容提取出來做對(duì)比
public class XmlBeanDefinitionReader { public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; } }
resolve
1.加載指定的 NamespaceHandler 映射,并且提取的 NamespaceHandler 緩存起來,然后返回
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)簽解。
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)單來說就是從 parsers 中尋找到 ApplicationBeanDefinitionParser 實(shí)例,并調(diào)用其自身的 doParse 方法進(jìn)行進(jìn)一步解析。最后就跟解析默認(rèn)標(biāo)簽的套路一樣了…
看完了這篇文章,相信你對(duì)“Spring源碼解密之自定義標(biāo)簽的示例分析”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。