一、自定义标签,自定义标签在使用上面相对来说非常常见了,这个也算是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. 百度地图label样式修正

    现象:百度地图 label 宽度为0,文字在标签边框外. 原因:样式冲突,在css中添加下列代码即可: .BMapLabel{ max-width:none; }

  2. ListView实现分页加载(二)实现底布局

    上一篇中,我们搭建好了一个Demo.没有阅读的可以点击下面的链接: http://www.cnblogs.com/fuly550871915/p/4866929.html 在这一篇中,我们将实现Lis ...

  3. 总结一下MVC思想

    MVC (Model View Controler)本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器.使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用 ...

  4. SOJ 1002/1003/1004 大整数相加/相乘/相除

    三个题目分别考察大整数相加相乘相除运算.如果按照传统算法是取一个长数组,之后进行模拟或者FFT来进行运算.但是相对繁琐. 后来昨天的青岛区域赛网赛1001,用到了JAVA的BigDecimal,于是反 ...

  5. rfcn校招总结

    idea:ROI pooling前都是卷积,是具备平移不变性的,但一旦插入ROI pooling之后,后面的网络结构就不再具备平移不变性了,就解决了分类和定位的矛盾,但因为引入roi-wise lay ...

  6. Multiply Strings 字符串相乘

    http://www.cnblogs.com/TenosDoIt/p/3735309.html https://blog.csdn.net/fly_yr/article/details/4805561 ...

  7. 提交文件到ng-pages分支

    一.提交dist文件夹到ng-pages分支 git subtree push --prefix=dist origin gh-pages 二.提交所有文件到ng-pages分支 text git:( ...

  8. 用python实现ping

    #!/usr/bin/env python #coding=utf-8 import os import argparse import socket import struct import sel ...

  9. building for production...Killed

    npm run build报错 building for production...Killed 原理 按照他人的说法是,服务器内存不够用了,这样就给他配置一个单独的内存出来就解决了 解决方法 sud ...

  10. HDU 2717 Catch That Cow(常规bfs)

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2717 Catch That Cow Time Limit: 5000/2000 MS (Java/Oth ...