Spring 配置文件自定义标签的前置条件

在上一篇文章https://www.cnblogs.com/redwinter/p/16165274.html Spring BeanFactory的创建过程中了解了BeanDefinition的加载和BeanFactory的创建,并且提到了Spring留了一个扩展点就是用户可以自定义标签进行解析BeanDefinition

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

定义标签属性类

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

/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @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 <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @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 <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @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文件

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://www.redwinter.com/schema/redwinter"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.redwinter.com/schema/redwinter"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="dl">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="username" type="xsd:string" use="required"/>
<xsd:attribute name="email" type="xsd:string" use="required"/>
<xsd:attribute name="password" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>

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

编写spring.schemas和spring.handlers文件

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

  • 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的文件进行配置

<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:redwinter="http://www.redwinter.com/schema/redwinter"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.redwinter.com/schema/redwinter http://www.redwinter.com/schema/redwinter.xsd
">
<!--自定义标签-->
<redwinter:dl id ="redwinter" email="abc@qq.com" password="123456" username="冬玲记忆"/>
<redwinter:dl id ="redwinter123456" email="123456-abc@qq.com" password="123456" username="冬玲记忆"/> </beans>

验证是否配置正确:

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刷新的前戏准备。

Spring 源码(4)在Spring配置文件中自定义标签如何实现?的更多相关文章

  1. Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  2. Spring源码-AOP部分-Spring是如何对bean实现AOP代理的

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 历史文章 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] S ...

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

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

  4. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  5. spring源码深度解析—Spring的整体架构和环境搭建

    概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ...

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

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

  7. spring源码学习(三)--spring循环引用源码学习

    在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例 @Component public class Add ...

  8. 【spring源码学习】spring的IOC容器之自定义xml配置标签扩展namspaceHandler向IOC容器中注册bean

    [spring以及第三方jar的案例]在spring中的aop相关配置的标签,线程池相关配置的标签,都是基于该种方式实现的.包括dubbo的配置标签都是基于该方式实现的.[一]原理 ===>sp ...

  9. Spring源码分析:Spring IOC容器初始化

    概述: Spring 对于Java 开发来说,以及算得上非常基础并且核心的框架了,在有一定开发经验后,阅读源码能更好的提高我们的编码能力并且让我们对其更加理解.俗话说知己知彼,百战不殆.当你对Spri ...

随机推荐

  1. Hadoop-全分布式配置

    目录 一.配置基础环境 1.配置网络信息 2.配置主机名 3.主机名与IP的映射关系 4.测试互通性 二.关闭防火墙和SELinux 1.关闭防火墙 2.关闭SELinux 三.安装 Hadoop 1 ...

  2. 半吊子菜鸟学Web开发 -- PHP学习3-文件

    目录 1 PHP文件系统 1.1 PHP文件的读取 1.4 获得文件的大小 1.5 PHP写入文件 1.6 删除文件 1 PHP文件系统 1.1 PHP文件的读取 文件读取的函数是file_get_c ...

  3. Vue指令02——v-on指令和v-show的使用

    Vue指令02--v-on指令和v-show的使用 v-on指令 格式1:v-on:事件="方法" 格式2:@事件="方法" 格式3:@事件="方法( ...

  4. 为什么要用 Spring Boot?

    Spring Boot 优点非常多,如:独立运行简化配置自动配置无代码生成和XML配置应用监控上手容易Spring Boot 集这么多优点于一身,还有理由不使用它呢?

  5. 数组有没有 length()方法?String 有没有 length()方法?

    数组没有 length()方法,有 length 的属性. String 有 length()方法.JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混 ...

  6. 重载(Overload)和重写(Override)的区别。重载的 方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性.重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则视 ...

  7. spring-boot-learning-配置文件相关

      Spring-boot的配置文件   springboot通过那个下面的依赖去读取配置文件的上下文: <!-- https://mvnrepository.com/artifact/org. ...

  8. 表单属性method的值get和post的区别?什么时候用get?什么时候用post?

    get和post的区别 一.安全性 因为get会将用户名和密码放在URL中,进而出现在浏览器的历史记录中,显然这种情况应该用post. 二.编码 get只能向服务器发送ASCII字符,而post则可以 ...

  9. hadoop 分布式系统与Hadoop MapReduce

    Hadoop分为两部分 Hadoop MapReduce和Hadoop分布式文件系统 1分布式系统由Master Node 和多台 slave Node组成. 1.1MasterNode Master ...

  10. Citus 分布式 PostgreSQL 集群 - SQL Reference(查询处理)

    一个 Citus 集群由一个 coordinator 实例和多个 worker 实例组成. 数据在 worker 上进行分片和复制,而 coordinator 存储有关这些分片的元数据.向集群发出的所 ...