中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring源碼解密之自定義標簽的示例分析

發布時間:2021-07-19 09:44:44 來源:億速云 閱讀:118 作者:小新 欄目:編程語言

小編給大家分享一下Spring源碼解密之自定義標簽的示例分析,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

自定義標簽

在講解 自定義標簽解析 之前,先看下如何自定義標簽

定義 XSD 文件

定義一個 XSD 文件描述組件內容

<?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 就行了。

  • 定義復合元素: 這里的 application 就是元素的名稱,使用時 <battcn:application id="battcn"/>

  • 定義元素屬性: 元素屬性就是 attribute 標簽,我們聲明了一個必填的 name 的屬性,使用時 <battcn:application id="battcn" name="Levin"/>

定義解析規則

1.創建一個類實現 BeanDefinitionParser 接口(也可繼承 Spring 提供的類),用來解析 XSD 文件中的定義和組件定義

public class ApplicationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
 @Override
 protected Class getBeanClass(Element element) {
 // 接收對象的類型 如: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);
 }
}

這里創建了一個 ApplicationBeanDefinitionParser 繼承 AbstractSingleBeanDefinitionParser(是:BeanDefinitionParser 的子類), 重點就是重寫的 doParse,在這個里面解析 XML 標簽的,然后將解析出的 value(Levin) 通過構造器方式注入進去

2.創建一個類繼承 NamespaceHandlerSupport 抽象類

public class BattcnNamespaceHandler extends NamespaceHandlerSupport {
 @Override
 public void init() {
 registerBeanDefinitionParser("application", new ApplicationBeanDefinitionParser());
 }
}

BattcnNamespaceHandler 的作用特別簡單,就是告訴 Spring 容器,標簽 <battcn:application /> 應該由那個解析器解析(這里是我們自定義的:ApplicationBeanDefinitionParser),負責將組件注冊到 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.使用自定義標簽

申明 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>

創建一個測試類,如果看到控制臺輸出了 Levin 字眼,說明自定義標簽一切正常

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.如圖所示

Spring源碼解密之自定義標簽的示例分析

源碼分析

自定義標簽解析入口

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 中注冊的 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));
 }
}

與默認標簽解析規則一樣的是,都是通過 getNamespaceURI(Node node) 來獲取命名空間,那么 this.readerContext.getNamespaceHandlerResolver() 是從哪里獲取的呢?我們跟蹤下代碼,可以發現在項目啟動的時候,會在 XmlBeanDefinitionReader 將所有的 META-INF/spring.handles 文件內容解析,存儲在 handlerMappers(一個ConcurrentHashMap) 中,在調用 resolve(namespaceUri) 校驗的時候在將緩存的內容提取出來做對比

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");
 }
 // 根據命名空間尋找對應的信息
 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);
 }
 }
 }
}

標簽解析

加載完 NamespaceHandler 之后,BattcnNamespaceHandler 就已經被初始化為 了,而 BattcnNamespaceHandler 也調用了 init() 方法完成了初始化的工作。因此就接著執行這句代碼: handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 具體標簽解。

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;
 }
}

簡單來說就是從 parsers 中尋找到 ApplicationBeanDefinitionParser 實例,并調用其自身的 doParse 方法進行進一步解析。最后就跟解析默認標簽的套路一樣了…

看完了這篇文章,相信你對“Spring源碼解密之自定義標簽的示例分析”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

克拉玛依市| 常德市| 象山县| 吴旗县| 沅江市| 涞水县| 弥勒县| 万载县| 牙克石市| 中方县| 桑日县| 宁武县| 神池县| 丰都县| 四子王旗| 稻城县| 隆尧县| 长丰县| 桑植县| 西藏| 大连市| 铜鼓县| 上林县| 青海省| 贵南县| 绿春县| 枣庄市| 东明县| 泉州市| 手机| 广州市| 安阳市| 应用必备| 北辰区| 肇庆市| 新绛县| 华蓥市| 余姚市| 子洲县| 开鲁县| 沁阳市|