文章目录

  • 前言
  • 总结
  • 0 项目结构
  • 1. 项目准备
    • 1.1 新建gradle项目
    • 1.2 新建spring配置文件
    • 1.3新建启动类
  • 2. 开始自定义标签
    • 2.1 确定需求
    • 2.2 建立实体类和具体解析类
    • 2.3 新建blog标签解析类
    • 2.4 新建NamespaceHandler
    • 2.5 添加资源文件
      • 2.5.1 添加spring.handlers
      • 2.5.2 添加spring.schemas文件
      • 2.5.3 添加cxyxh.xsd文件
  • 3.使用自定义标签
    • 3.1 修改配置文件
    • 3.2 执行启动类
  • 4. 扩展
  • 5. 错误
    • 5.1 id属性是必须的
      • 5.1.1 具体解析
    • 5.2在源码中进行自定义标签测试时

前言

尝试在spring中自定义标签。需求:我需要在spring中添加一个自定义标签cxyxh:blog将属性读取到BeanFactory中,并能通过指定的id获取到Bean。


总结

最终成功的引入了自定的标签 cxyxh:blog。

0 项目结构

软件及版本:

​ idea:2020.2

​ gradle: 6.3

​ spring: 5.2.9.RELEASE

1. 项目准备

1.1 新建gradle项目

在build.gradle文件中引入,阿里云的maven仓库地址和spring-context的依赖。

添加阿里云的依赖:maven { url "https://maven.aliyun.com/repository/public" }之后下载jar包速度会快一点

本次因为只是测试自定义标签,所以只需要引入spring-context的依赖就行。使用的版本是5.2.9的版本

spring-context依赖又依赖了,spring-bean和spring-core。所以引入spring-context,相当于三个都引入了。

plugins {id 'java'
}group 'org.example'
version '1.0-SNAPSHOT'repositories {maven { url "https://maven.aliyun.com/repository/public" }mavenCentral()
}dependencies {testCompile group: 'junit', name: 'junit', version: '4.12'// https://mvnrepository.com/artifact/org.springframework/spring-contextcompile group: 'org.springframework', name: 'spring-context', version: '5.2.9.RELEASE'
}

1.2 新建spring配置文件

resource/config/spring-config.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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

1.3新建启动类

Main.java

package com.cxyxh.custom.tag;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/config/spring-config.xml");Object personController = applicationContext.getBean("blog");System.out.println(personController);}
}

启动类建立完成之后可以尝试启动一下,输出为null。

2. 开始自定义标签

2.1 确定需求

需要支持 <cxyxh:blog id="blog" uri="blog.cxyxh.cn" protocol="https" port="443"/>标签的解析。

定下namespace为“http://www.cxyxh.cn/schemas/cxyxh”。这里可以自定义随便字符串。

2.2 建立实体类和具体解析类

Blog.java

因为需求中标签一共有4个字段,但是我们只需要三个,id只是一个标识符。新建一个Blog.java的实体类,属性有:uri,protocol,port。然后设置他的setter/getter方法以及toString()方法

package com.cxyxh.custom.tag.sleftag;public class Blog {private String uri;private String protocol;private Integer port;public String getUri() {return uri;}public void setUri(String uri) {this.uri = uri;}public String getProtocol() {return protocol;}public void setProtocol(String protocol) {this.protocol = protocol;}public Integer getPort() {return port;}public void setPort(Integer port) {this.port = port;}@Overridepublic String toString() {return "Blog{" +"uri='" + uri + '\'' +", protocol='" + protocol + '\'' +", port=" + port +'}';}
}

2.3 新建blog标签解析类

需求中的标签,只需要将值读出来,然后存入Bean中就行,不需要其他操作。

需要实现getBeanClass()获取这个标签解析类解析的标签属于哪个类。

实现doParse()具体实现解析标签,然后存入BeanDefinition中。
可以选择实现shouldGenerateId()方法,用于自动生成id。

package com.cxyxh.custom.tag.sleftag;import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;public class BlogBeanDefinitonParser extends AbstractSingleBeanDefinitionParser {@Overrideprotected Class<?> getBeanClass(Element element) {return Blog.class;}@Overrideprotected void doParse(Element element, BeanDefinitionBuilder builder) {String uri = element.getAttribute("uri");String port = element.getAttribute("port");String protocol = element.getAttribute("protocol");if (StringUtils.hasText(uri)){builder.addPropertyValue("uri", uri);}if (StringUtils.hasText(port)){builder.addPropertyValue("port", Integer.valueOf(port));}if (StringUtils.hasText(protocol)){builder.addPropertyValue("protocol", protocol);}}
}

2.4 新建NamespaceHandler

NamespaceHandler接口会在解析自定义标签时使用。

需要实现init()方法,然后调用registerBeanDefinitionParse()将 标签名 以及对应的解析类注册到NamespaceHandlerSupport中,会在后续解析自定义标签是,根据标签名获取具体的解析类来解析标签。

所以在需求中的标签是blog,此处registerBeanDefinitionParse()方法的第一个参数就是"blog"。

package com.cxyxh.custom.tag.sleftag;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;public class CxyxhNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("blog", new BlogBeanDefinitonParser());}
}

2.5 添加资源文件

2.5.1 添加spring.handlers

这是一个properties类型的文件,key是之前我们定下的namespace,但是“:”需要转义,而value值对应就是之前新建的NamespaceHandler类的全限定名。

http\://www.cxyxh.cn/schemas/cxyxh=com.cxyxh.custom.tag.sleftag.CxyxhNamespaceHandler

2.5.2 添加spring.schemas文件

这也是一个properties类型的文件:key是自己定的,会在spring的配置文件中用到,value值是xsd文件的相对地址。

http\://www.cxyxh.cn/schemas/cxyxh/cxyxh-1.0.xsd=META-INF/cxyxh.xsd

spring.handlers和spring.schemas这两个文件的文件名不区分大小写

2.5.3 添加cxyxh.xsd文件

此文件中定义的是 标签需要遵循的规范。指定了xmlns就是namespace,此处targetNamespace需要和xmlns值一样。注意在顶级标签中必须有一个id属性并且有值。id属性的值就是BeanName,用于取在容器中的Bean。

<xsd:element name="blog">的name中便是我们的标签<cxyxh:blog >中的blog

<xsd:attribute>中定义的就是标签能使用的属性值

<?xml version="1.0" encoding="UTF-8" standalone="no"?><xsd:schema xmlns="http://www.cxyxh.cn/schemas/cxyxh"xmlns:xsd="http://www.w3.org/2001/XMLSchema"targetNamespace="http://www.cxyxh.cn/schemas/cxyxh"elementFormDefault="qualified"><xsd:element name="blog"><xsd:complexType><xsd:attribute name="id" type="xsd:string"/><xsd:attribute name="uri" type="xsd:string"/><xsd:attribute name="protocol" type="xsd:string"/><xsd:attribute name="port" type="xsd:string"/></xsd:complexType></xsd:element>
</xsd:schema>

3.使用自定义标签

3.1 修改配置文件

<?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:cxyxh="http://www.cxyxh.cn/schemas/cxyxh"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.cxyxh.cn/schemas/cxyxhhttp://www.cxyxh.cn/schemas/cxyxh/cxyxh-1.0.xsd"><cxyxh:blog id="blog" uri="blog.cxyxh.cn" protocol="https" port="443"/>
</beans>

添加了:

xmlns:cxyxh="http://www.cxyxh.cn/schemas/cxyxh"

key中的cxyxh就是标签<cxyxh:blog>中的cxyxh, value对应的是namespace的值。

​ 在xsi:schemaLocation 指定 schema的位置。

http://www.cxyxh.cn/schemas/cxyxh
http://www.cxyxh.cn/schemas/cxyxh/cxyxh-1.0.xsd

第一个是指定的是spring.handlers中的key值,第二个是spring.schemas中的key值

3.2 执行启动类

执行结果为:

Blog{uri='blog.cxyxh.cn', protocol='https', port=443}

4. 扩展

如果还需要添加cxyxh下的其他标签,步骤如下

  1. 添加标签的解析类:***BeanDefinitionParser.java
  2. 在CxyxhNamespaceHandler中的init方法中,添加一条registerBeanDefinitionParser(),第一个参数是你需要添加的标签名,第二个是对应的解析类。
  3. 在cxyxh,xsd文件中,在添加一条新的element就行。

如果需要添加其他标签,重复本文的步骤就行。

5. 错误

5.1 id属性是必须的

提示消息为:Configuration problem: Id is required for element 'blog' when used as a top-level tag

错误是:需要一个id的key。所以检查在是spring-config.xml的自定义的标签中是否有id的属性。

5.1.1 具体解析

在spring解析完属性之后会尝试获取id属性,如果没有则报错。

报错位置:

获取id方法:resolveId()

这段代码就是自动生成一个id,但是和我们使用id指定的不一样值为:com.springframework.sourcecode.startioc.selftag.Blog#0

parserContext.getReaderContext().generateBeanName(definition);

所以如果不是需要单独的取得话,可以重写shouldGenerateId()方法,然后返回true,他会自动生成一个id。

5.2在源码中进行自定义标签测试时

本处错误出现在,源码中编译期间。

将gradle文件夹下的docs.gradle文件的第220行代码注释掉,这里对META-INF/spring.schemas文件做了特殊处理。将这个文件里的所有的key取出来,然后做特殊替换,然后做了一个断言,必须保证替换前和替换后不相等才能继续。

         def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')                      assert shortName != key//如果不满足这个条件则,不让过也就是,schema中的key的值和替换之后的值必须不一样。

主要目的应该是保证,源代码中只能解析spring的自己的标签。

处理方式就是:

​ 1、将本处断言关闭,注释掉

​ 2、将文件的文件名换成某个字母大写就行

Spring中添加自定义标签相关推荐

  1. Spring中bean标签的属性和值:

    Spring中bean标签的属性和值: <bean name="user" class="com.pojo.User" init-method=" ...

  2. 【Spring全家桶系列】Spring中bean标签的配置与使用

    ⭐️前面的话⭐️ 本文已经收录到<Spring框架全家桶系列>专栏(该专栏计划在全部文章更新完毕后付费,反正订阅不要钱,要不来一个?),本文将介绍Spring中XML配置Bean,有关be ...

  3. spring中默认标签Bean标签解析一

    在Spring种有两种标签,一种是默认标签,另一种是自定义标签.对两种标签的用法和解析方式存在着很大的不同. 首先分析的是默认标签的解析过程. 解析标签的入口代码 protected void par ...

  4. Spring中的p标签(转)good

    Spring的p标签是基于XML Schema的配置方式,目的是为了简化配置方式. 在XML文件头部添加xmlns:p="http://www.springframework.org/sch ...

  5. java 自定义xml_6.1 如何在spring中自定义xml标签

    dubbo自定义了很多xml标签,例如,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子. 一 编写模型类 1 packagecom.hulk.testdubbo.model ...

  6. spring中context:property-placeholder标签详解

    spring中context:property-placeholder标签的使用说明 1,有些参数在某些阶段中是常量. 在开发阶段我们连接数据库时的url,username,password等信息 分 ...

  7. 中input标签赋值_Java程序员:Spring MVC JSP表单标签示例

    Spring MVC的表单标签为Java程序员提供了许多额外的支持.例如数据绑定,允许自动设置数据并从Java对象中检索数据. 从2.0版本开始,Spring提供了一组全面的数据绑定感知标记,用于在使 ...

  8. spring中<tx:annotation-driven>标签转为注解@EnableTransactionManagement

    spring中涉及事务的内容使用注解开发时,需开启事务的注解驱动 <?xml version="1.0" encoding="UTF-8"?> &l ...

  9. Spring中部署Activiti流程定义的三种姿势

    摘要:本文对工作流Activiti框架中流程定义的部署进行了详细说明介绍. 本文分享自华为云社区<项目中工作流部署详细解析!Spring中部署Activiti流程定义的三种姿势>,作者:攻 ...

最新文章

  1. @程序员:这些瓜没吃到,可以告别互联网了!
  2. [Ubuntu] SVN常用的批量操作
  3. 简单探索MNIST(Softmax回归和两层CNN)-Tensorflow学习
  4. unity热更新json_Unity3D热更新 CSHotFix入门教程之HelloWorld
  5. python的数据类型和变量的定义及使用
  6. Boost:验证atomic <>不对函数指针提供算术运算
  7. 多进程服务器(python 版)
  8. 李飞飞:一站式全链路数据管理与服务成为产业布局重点
  9. matlab处理足球数据,如何用MATLAB画一个足球出来?
  10. Linux下编译googletest
  11. np.expand_dims
  12. 阿斯克码表java_一分钟了解阿斯克码
  13. matlab2014a如何画电机效率云图,maxwell电机转矩扫描与使用MTPA策略绘制效率map图...
  14. 微信支付-----扫码支付,统一下单
  15. 怎么用电脑屏幕录制功能录制游戏视频
  16. 数据科学风云之互联网金融
  17. win10计算机文件夹隐藏,Win10此电脑6个文件夹隐藏方法
  18. Otterctf 2018 内存取证
  19. 小米校招 C++研发 相机部 一二面
  20. Win7 x64动态开启DSE

热门文章

  1. 离均差oracle 函数,Oracle入门到精通
  2. 基于模块化的PLM系统设计研究
  3. kaze算法的图像配准研究(1)-KAZE算法原理
  4. 【OBS】解决OBS推两个rtmp流 + 带时间戳问题
  5. Android开发中的WMS详细解析
  6. 虚拟机——KSM Kernel Samepage Merging
  7. (实战)[re:Invent 2018]-001:赛道分析-(致敬1024)
  8. RJ45和RJ48的区别
  9. EasyExcel操作API与示例
  10. python expect模块pexpect简单应用