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

基于 Spring源码在处理定制的标签时是通过定制的命名空间处理器和 xsd文件进行解析的,在 springclasspath下的 META-INF/spring.schemasMETA-INF/spring.handlers,并且需要将标签的解析器注册到 BeanDefinition的解析器中,这样说起来比较抽象,接下来我们自己定义一个标签就明了了。

创建一个需要解析的标签的属性,比如在 Spring配置文件中经常看到的 <context:component-scan base-package="com.redwinter.test"></context:component-scan> ,这里的 component-scan就是标签属性。

/**
 * @author redwinter
 * @since 1.0
 **/
public class Redwinter {

    private String username;
    private String email;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

定义一个 Redwinter类,里面三个属性,当然你可以自己定义你需要的属性,我这里就随便写啦。

定义好标签的属性之后就需要定义一个解析器对这些属性进行解析,定义解析器需要继承 AbstractSingleBeanDefinitionParser,这个类是实现了 BeanDefinitionParser的类,他下面有很多实现类,一般来说我们的Bean都是单例的,那就继承 AbstractSingleBeanDefinitionParser即可。

/**
 * @author redwinter
 * @since 1.0
 **/
public class RedwinterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    @Override
    protected Class getBeanClass(Element element) {
        return Redwinter.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        /**
         * 自定义解析xml的自定义字段,并做相应的其他处理
         */
        String username = element.getAttribute("username");
        String email = element.getAttribute("email");
        String password = element.getAttribute("password");
        if (StringUtils.hasText(username)){
            builder.addPropertyValue("username",username);
        }
        if (StringUtils.hasText(email)){
            builder.addPropertyValue("email",email);
        }
        if (StringUtils.hasText(password)){
            builder.addPropertyValue("password",password);
        }
    }
}

这个解析器主要是重写了父类的两个方法,一个是 getBeanClass用于返回对应的标签属性类,一个是解析属性 doParser,这里我只是从 element中获取出来然后进行了下判断在加入到属性值中,当然这里你可以自定义自己的逻辑处理。

定义命名空间处理器需要继承NamespaceHandlerSupport,然后重写他的init方法,将解析器注册进去,这个解析器就是上面定义的用来解析标签属性的解析器。

/**
 * @author redwinter
 * @since 1.0
 **/
public class RedwinterNameSpaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        // 这里的属性必须和xsd中指定的属性一致,否则报错
        //org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [dl]
        registerBeanDefinitionParser("dl",new RedwinterBeanDefinitionParser());
    }
}

这里需要注意的是,进行注册时需要指定一个 elementName,这个值必须和xml中定义的名称一致,否者的话就会报如下错:

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [dl]

我这里定义的元素名称叫 dl

xsd文件就是spring进行xml解析时解析的标签,当然你可以定义dtd文件,不过现在一般都用xsd文件,我这里命名为redwinter.xsd,完整文件如下:


这里有几个点需要注意: schema标签下有个 targetNamespace,这里指定了命名空间叫http://www.redwinter.com/schema/redwinter ,那么在进行 spring配置文件的时候引入的 namespace就是这个,然后有个 name="dl",这里的这个 dl就是处理器中定义的元素名称,而且必须一致,不然解析不到,下面定义的属性就是标签属性类中定义的刷新,这个 id是表示唯一的 Bean名称。

这里直接列出完整文件内容:

  • spring.schemas文件
http\://www.redwinter.com/schema/redwinter.xsd=META-INF/redwinter.xsd

这里需要注意的是,这里配置的 key也是需要在 spring配置文件中引入的, value就是上一步定义的 xsd文件所在路径

  • spring.handlers文件
http\://www.redwinter.com/schema/redwinter=com.redwinter.test.RedwinterNameSpaceHandler

这里配置的 key就是上一步定义的 xsd文件中定义的 targetNamespacevalue就是定义的命名空间处理器。

到这里自定义标签和解析就完成了,最后就需要在spring配置文件中引入并配置。

我这里定义个角 spring-test.xml的文件进行配置


验证是否配置正确:

public class BeanCreate {

    @Test
    public void classPathXml() {
//      ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-test.xml");
        ClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("classpath:spring-test.xml");

        Redwinter redwinter = (Redwinter) context.getBean("redwinter");
        System.out.println(redwinter.getEmail());

        Redwinter redwinter123456 = (Redwinter) context.getBean("redwinter123456");
        System.out.println(redwinter123456.getEmail());
    }
}

输出:

abc@qq.com
123456-abc@qq.com

那说明自定义标签生效了,并且成功解析出来。

接下来就是继续介绍 Spring 容器的实现 AbstractApplicationContex#refresh的第三个方法,这个方法其实就是 BeanFactory使用的前戏准备,而第一个方法是 BeanFactory刷新的前戏准备。

Original: https://www.cnblogs.com/redwinter/p/16165878.html
Author: 玲丶蹊
Title: Spring 源码(4)在Spring配置文件中自定义标签如何实现?

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/610058/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • SpringBoot2整合Junit4和Junit5

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月8日
    095
  • 250_RabbitMQ-集群监控

    集群监控 管理界面监控 tracing日志监控 消息追踪启用与查看 日志追踪 定制自己的监控系统 Zabbix 监控RabbitMQ 集群监控 :::info在广大的互联网行业中R…

    Java 2023年6月7日
    0106
  • 数据库学习记录(六)

    &#x6F14;&#x793A;&#x4E8B;&#x52A1;&#xFF1A;* mysql&#x4E8B;&#x52A1…

    Java 2023年6月7日
    067
  • java高级-续1

    IO 所谓IO就是输出输出(input/output)。一般的理解都是相对于计算机而言的输入输出。 比如: 输出设备:显示器,耳机,音响,打印机….. 输入设备:键盘,…

    Java 2023年6月7日
    0115
  • java调用python脚本,生成excel

    java: 1 /** 2 * 使用python创建excel并且输出 3 * @throws Exception 4 */ 5 public void pyExportExcel…

    Java 2023年6月16日
    073
  • sqlserver数据库还原存储过程脚本

    &#x5B58;&#x50A8;&#x8FC7;&#x7A0B;&#x5FC5;&#x987B;&#x8981;&#…

    Java 2023年6月7日
    075
  • java基础

    深入循环结构 for(循环条件1) { //循环操作1 for(循环条件2) { //循环操作2 } } 多层循环: 外层循环变量变化一次,内层循环变量要变化一轮。 一、循环打印输…

    Java 2023年6月13日
    073
  • MQ 简介

    You must try things that may not work. And you must not let anyone define your limits beca…

    Java 2023年6月9日
    055
  • Spring Boot 开发集成 WebSocket,实现私有即时通信系统

    1/ 概述 利用Spring Boot作为基础框架,Spring Security作为安全框架,WebSocket作为通信框架,实现点对点聊天和群聊天。 2/ 所需依赖 Sprin…

    Java 2023年5月30日
    081
  • 精华 k8s 入门安装配置并部署nginx

    k8s 搭建 1, 关闭 swap 内存 确保禁止掉swap分区 K8s的要求,在每个宿主机上执行: sudo swapoff -a #修改/etc/fstab,注释掉swap那行…

    Java 2023年5月30日
    092
  • Java学习 (19) Java数组篇(03)数组的使用

    数组使用 数组使用一般分四种情况 1.普通 For 循环 2.For-Each 循环 (增强For循环) 3.数组作方法入参 4.数组作返回值 语法实例 多维数组 语法实例 数组使…

    Java 2023年6月8日
    096
  • Java基础语法(一)

    Java基础语法(一) 道阻且长,行则将至,行而不辍,未来可期。 ——《荀子·修身》 Java基础语法(一) – 一、Java注释 二、关键字和保留字 三、标识符 四、…

    Java 2023年6月9日
    069
  • 11.如何把一段逗号分隔的字符串转换为一个数组

    split public class DouHaoShuZuFenGe { public static void main(String[] args) { String orgS…

    Java 2023年6月9日
    086
  • 高德坐标系转wgs(苹果坐标系) java代码

    private static double PI = 3.14159265358979324;public static double[] gcj02ToWgs(double ln…

    Java 2023年5月29日
    0100
  • 记录一下今天所学 9.22

    今天上午在公司没啥任务,就学起了es,看的黑马的资料,先看文档,不懂的地方就去看了下视频。 大概知道了es概念,es是es技术栈中最核心的,这个技术栈还有其他的比如分词器插件,还有…

    Java 2023年6月15日
    078
  • 表单的子元素可不在form标签内

    表单是网页用于向服务器发送数据的元素。其用法类似下面: form标签对及其内部的所有子元素共同组成了表单。提交表单时,浏览器会将form标签对内所有具有name属性的标签的键值提交…

    Java 2023年6月6日
    0100
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球