一、自定义标签,自定义标签在使用上面相对来说非常常见了,这个也算是spring对于容器的拓展。通过自定义标签的方式可以创造出很多新的配置方式,并且交给容器直接管理,不需要人工太多的关注。这也是spring对于配置拓展的一个很重要的方式。

  二、自定义标签的几个步骤:1、创建可扫描的标签和对应的解析类  2、读取页面元素解析 3、加入容器管理

  三、涉及到的常用类:BeanDefinitionParser、NamespaceHandlerSupport;文件:spring.handlers、spring.schemas、*.xsd

  四、实现过程:

  1)需要实现的类:

  

  pojo:

public class User{

    private String id;
private String name;
private String age; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getAge() {
return age;
} public void setAge(String age) {
this.age = age;
} }

  UserBeanDefinitionParser:

public class UserBeanDefinitionParser implements BeanDefinitionParser {

    //解析xml
public BeanDefinition parse(Element element, ParserContext parserContext) {
//通过读取xml的元素来实现具体配置
String id = element.getAttribute("id");
String name = element.getAttribute("name");
String age = element.getAttribute("age");
//注册到spring容器
BeanDefinitionRegistry registry = parserContext.getRegistry();
BeanDefinition beanDefinition = null;
try {
//这里的RootBeanDefinition为我们常用的注册形式
beanDefinition = new RootBeanDefinition(User.class);
beanDefinition.getPropertyValues().add("id", id);
beanDefinition.getPropertyValues().add("name", name);
beanDefinition.getPropertyValues().add("age", age);
//注册
registry.registerBeanDefinition(id, beanDefinition);
} catch (Exception e) {
e.printStackTrace();
}
return beanDefinition;
}
}

  UserNameSpaceHandler:

//用于提供namespace的扫描
public class UserNameSpaceHandler extends NamespaceHandlerSupport { //初始化
public void init() {
//注册解析手段
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
}
}

  2)因为来了,这个没有啥关联啊。那么久需要用到具体的配置来做关联(spring.handlers、spring.schemas、*.xsd)

  

  备注:这个自己默认配置在resources里面就可以。目录必须为META-INF,默认两个文件:spring.handlers、spring.schemas。标签:*.xsd

  spring.handlers:默认处理的NamespaceHandlerSupport

http\://www.pinnet.com/schema/user=com.pinnet.customLabel.UserNameSpaceHandler

  spring.schemas:用于使用的标签格式以及验证

http\://www.pinnet.com/schema/user.xsd=META-INF/user.xsd

  user.xsd:这里写了一个简单的例子(标签的编写这里不做介绍,可以自己查询schema的官网

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://www.pinnet.com/schema/user"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.pinnet.com/schema/user"
elementFormDefault="qualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:element name="user">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="age" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>

  3)好了上面的基本准备工作ok了。具体就是容器做的处理了,我们来关注源码的实现部分。

  spring-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:pinnet="http://www.pinnet.com/schema/user" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.pinnet.com/schema/user http://www.pinnet.com/schema/user.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<pinnet:user id="user1" name="name1" age="age1"/>
<pinnet:user id="user2" name="name2" age="age2"/>
<pinnet:user id="user3" name="name3" age="age3"/>
</beans>

  注意:有下划线的部分,这里就是引用过后,然后需要进行的配置。xmlns:pinnet中的pinnet可以自己随便取

  测试:

public class Test {

    public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
User user1 = (User) context.getBean("user1");
User user2 = (User) context.getBean("user2");
User user3 = (User) context.getBean("user3");
System.out.println(user1);
System.out.println(user2);
System.out.println(user3);
}
}

  4)好了重点来了,源码部分。

  这里从自定义标签开始讲起:其他部分可以参考:spring源码-bean之初始化-1到7)部分

  parseCustomElement

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//判断根节点是否是默认的(也就是spring提供的beans)
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
//这里就是自定义的配置方式
delegate.parseCustomElement(ele);
}
}
}
} else {
//如果根节点不是自定义的那就自己自定义处理
delegate.parseCustomElement(root);
}
}
public BeanDefinition parseCustomElement(Element ele) {
//解析配置
return this.parseCustomElement(ele, (BeanDefinition)null);
} public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取Namespace的uri
String namespaceUri = this.getNamespaceURI(ele);
//然后获取NamespaceHandler的实现类NamespaceHandlerSupport的对应实现
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
} else {
//处理、解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}

  resolve:

public NamespaceHandler resolve(String namespaceUri) {
//获取所有NamespaceHandler的NamespaceHandlerSupport实现类(过程不详解了)
Map<String, Object> handlerMappings = this.getHandlerMappings();
//获取具体的NamespaceHandlerSupport实现类
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
} else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler)handlerOrClassName;
} else {
//我们默认第一次通过spring.handlers,一般都是String类型的
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");
} else {
//获取提前加入容器的NamespaceHandler
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
//调用初始化这里的初始化查看前面的调用
namespaceHandler.init();
//加入缓存
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
} catch (ClassNotFoundException var7) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
} catch (LinkageError var8) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
}
}
}

  parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
//发现解析的BeanDefinitionParser,并调用实现类的parse方法
return this.findParserForElement(element, parserContext).parse(element, parserContext);
} private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
//获取本地的localName,在NamespaceHandlerSupport中进行了init,所以会直接获取到UserNameSpaceHandler
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
} return parser;
}

  五:上面基本的实现过程就是这样子了,包含了源码的实现逻辑。这里需要关注的点在于匹配的过程。其他都是自己手动完成的。

  六、用处:相对于我们人的解析过程,自定义标签的解析的过程并不是很复杂,更多需要自己手动去完成解析。自定义标签的好处在于,可以很大程度的减少代码冗余的情况。通过同一套流程,开发者只需要关注配置就可以了,而不需要关注具体的解析过程和实现逻辑。另外可以更加方便的与spring容器进行深度的整合!

  

spring源码-自定义标签-4的更多相关文章

  1. spring源码-aop源码-5.1

    一.aop的源码部分还是有点复杂的,但是为了更好的理解,我这里会省去很多不必要的逻辑实现过程.主要方向还是更好的理解整体代码的实现过程. 二.说明重点:aop的过程主要过程有两点:第一点,发现正确和适 ...

  2. spring源码-开篇

    一.写博客也有一段时间了,感觉东西越来越多了,但是自己掌握的东西越来越少了,很多时候自己也在想.学那么多东西,到头来知道的东西越来越少了.是不是很奇怪,其实一点都不奇怪. 我最近发现了一个很大的问题, ...

  3. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  4. Spring源码分析(九)解析默认标签中的自定义标签元素

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 到这里我们已经完成了分析默认标签的解析与提取过程,或许涉及的内容太多,我 ...

  5. Spring 源码学习(1) —— 自定义标签

    Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签.解析默认标签调用: private void parseDefaultElement(Element el ...

  6. Spring源码学习——自定义标签

    2019独角兽企业重金招聘Python工程师标准>>> 1.自定义标签步骤 创建一个需要扩展的组件 定义xsd文件描述组件内容 创建一个文件,实现BeanDefinitionPars ...

  7. 从零开始学spring源码之xml解析(二):默认标签和自定义标签解析

    默认标签: 上一篇说到spring的默认标签和自定义标签,发现这里面东西还蛮多的.决定还是拆开来写.今天就来好好聊聊这两块是怎么玩的,首先我们先看看默认标签: private void parseDe ...

  8. Spring 源码(4)在Spring配置文件中自定义标签如何实现?

    Spring 配置文件自定义标签的前置条件 在上一篇文章https://www.cnblogs.com/redwinter/p/16165274.html Spring BeanFactory的创建过 ...

  9. Spring源码情操陶冶-自定义节点的解析

    本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...

随机推荐

  1. Linux系统如何禁止普通用户切换root?

    Linux系统如何禁止普通用户切换root? 在上正文之前,我们先将一些基础的Linux用户以及用户组的相关命令: 1.添加用户 useradd [-g group] [-d user_home_di ...

  2. 压缩Windows系统磁盘

    compact /compactOS:always #压缩所有 OS 二进制文件并将系统状态设置为"压缩" compact /compactOS:never #减压缩所有 OS 二 ...

  3. h5做的app和原生app的区别

    之所以说h5做的app和原生app的区别,是因为一位博友的问题: 随着 h5 的普及,是不是不再需要开发 app ? 我的回答是要分业务需求,分场合而定. 比如现在的微信小程序这么流行,甚至也取代了不 ...

  4. Node.js发布http服务

    Node.js发布http服务 2018-11-09 09:43:03   Visit  0 简单服务 var http = require(\'http\'); http.createServer( ...

  5. PAT——有几个PAT

    思路来源:https://www.cnblogs.com/asinlzm/p/4440603.html 字符串APPAPT中包含了两个单词“PAT”,其中第一个PAT是第2位(P),第4位(A),第6 ...

  6. VS2013 类向导 "异常来自 HRESULT:0x8CE0000B" 解决方法

    转自 http://blog.csdn.net/skyloveyue/article/details/52105912 我用使用了第二种方法: 改变项目的位置 将项目从原来D盘的位置(D:\proje ...

  7. Visual Studio 中常用的快捷键

    项目相关的快捷键 Ctrl + Shift + B = 生成项目 Ctrl + Alt + L = 显示 Solution Explorer(解决方案资源管理器) Shift + Alt+ C = 添 ...

  8. 微信小程序获取手机信息

    wx.getSystemInfo({ success: function (res) { console.log(res.model)//手机型号 console.log(res.pixelRatio ...

  9. Django开发BUG汇总

    使用版本知悉 limengjiedeMacBook-Pro:~ limengjie$ python --version Python :: Anaconda, Inc. limengjiedeMacB ...

  10. iOS 从零到一搭建组件化项目框架

    随着公司业务需求的不断迭代发展,工程的代码量和业务逻辑也越来越多,原始的开发模式和架构已经无法满足我们的业务发展速度了,这时我们就需要将原始项目进行一次重构大手术了.这时我们应该很清晰这次手术的动刀口 ...