最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring源碼解密之自定義標(biāo)簽與解析

 更新時(shí)間:2018年01月15日 10:52:48   作者:唐亞峰  
這篇文章主要給大家介紹了關(guān)于Spring源碼解密之自定義標(biāo)簽與解析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。

前言

在 上一節(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)容

<?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 就是元素的名稱(chēng),使用時(shí) <battcn:application id="battcn"/>
  • 定義元素屬性: 元素屬性就是 attribute 標(biāo)簽,我們聲明了一個(gè)必填的 name 的屬性,使用時(shí) <battcn:application id="battcn" name="Levin"/>

定義解析規(guī)則

1.創(chuàng)建一個(gè)類(lèi)實(shí)現(xiàn) BeanDefinitionParser 接口(也可繼承 Spring 提供的類(lèi)),用來(lái)解析 XSD 文件中的定義和組件定義

public class ApplicationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
 @Override
 protected Class getBeanClass(Element element) {
 // 接收對(duì)象的類(lèi)型 如: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 的子類(lèi)), 重點(diǎn)就是重寫(xiě)的 doParse,在這個(gè)里面解析 XML 標(biāo)簽的,然后將解析出的 value(Levin) 通過(guò)構(gòu)造器方式注入進(jìn)去

2.創(chuàng)建一個(gè)類(lèi)繼承 NamespaceHandlerSupport 抽象類(lèi)

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.編寫(xiě) 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è)試類(lèi),如果看到控制臺(tái)輸出了 Levin 字眼,說(shuō)明自定義標(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ī)則一樣的是,都是通過(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ì)比

public class XmlBeanDefinitionReader {
 public NamespaceHandlerResolver getNamespaceHandlerResolver() {
 if (this.namespaceHandlerResolver == null) {
 this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
 }
 return this.namespaceHandlerResolver;
 }
}

resolve

1.加載指定的 NamespaceHandler 映射,并且提取的 NamespaceHandler 緩存起來(lái),然后返回

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)單來(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ì)腳本之家的支持。

說(shuō)點(diǎn)什么

全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter2

相關(guān)文章

  • SpringBoot+Redis+Lua分布式限流的實(shí)現(xiàn)

    SpringBoot+Redis+Lua分布式限流的實(shí)現(xiàn)

    本文主要介紹了SpringBoot+Redis+Lua分布式限流的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • java集合類(lèi)源碼分析之Set詳解

    java集合類(lèi)源碼分析之Set詳解

    下面小編就為大家?guī)?lái)一篇java集合類(lèi)源碼分析之Set詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Java?I/O流使用示例詳解

    Java?I/O流使用示例詳解

    Java.io?包幾乎包含了所有操作輸入、輸出需要的類(lèi)。所有這些流類(lèi)代表了輸入源和輸出目標(biāo)。本文將通過(guò)示例為大家詳細(xì)講講?I/O流的使用教程,需要的可以參考一下
    2022-08-08
  • Java實(shí)現(xiàn)的自定義類(lèi)加載器示例

    Java實(shí)現(xiàn)的自定義類(lèi)加載器示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的自定義類(lèi)加載器,結(jié)合具體實(shí)例形式分析了java自定義類(lèi)加載器的原理與具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-07-07
  • Java8中StringJoiner類(lèi)的使用詳解

    Java8中StringJoiner類(lèi)的使用詳解

    Java在java.util包中添加了一個(gè)新的最終類(lèi)StringJoiner??梢杂糜跇?gòu)造由定界符分隔的字符序列。本文將通過(guò)示例和大家分享一下StringJoiner類(lèi)的使用,需要的可以參考一下
    2022-10-10
  • SpringMVC Cron定時(shí)器Demo常見(jiàn)問(wèn)題解決方案

    SpringMVC Cron定時(shí)器Demo常見(jiàn)問(wèn)題解決方案

    這篇文章主要介紹了SpringMVC Cron定時(shí)器Demo常見(jiàn)問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例

    Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例

    這篇文章主要介紹了Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Spring MVC異常處理機(jī)制示例詳解

    Spring MVC異常處理機(jī)制示例詳解

    這篇文章主要給大家介紹了關(guān)于Spring MVC異常處理機(jī)制的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring MVC具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Spring Boot 功能整合的實(shí)現(xiàn)

    Spring Boot 功能整合的實(shí)現(xiàn)

    Spring Boot生態(tài)豐富,集成也不算困難。本文簡(jiǎn)單的介紹下功能整合的步驟,最后提供一個(gè)具體的實(shí)現(xiàn)例子,學(xué)習(xí)Spring Boot的同學(xué)可以參考下
    2021-05-05
  • Java 如何在switch case語(yǔ)句中聲明變量

    Java 如何在switch case語(yǔ)句中聲明變量

    這篇文章主要介紹了Java 如何在switch case語(yǔ)句中聲明變量,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評(píng)論

大洼县| 和林格尔县| 仪征市| 岚皋县| 九台市| 和平县| 思南县| 江阴市| 湟中县| 云龙县| 益阳市| 阜新市| 万全县| 潞城市| 北票市| 军事| 桐柏县| 台东市| 襄汾县| 南昌市| 青海省| 柳林县| 崇礼县| 吕梁市| 昌吉市| 邵阳市| 简阳市| 武义县| 内乡县| 靖边县| 灌云县| 阳东县| 隆化县| 镶黄旗| 阿图什市| 门源| 安丘市| 陆丰市| 雅安市| 云安县| 赣州市|