原创

Spring中自定义xml标签

文章源码:https://github.com/Sniper2016/SpringStudy

1.自定义标签的用途

自定义标签可以说是 spring 提供的最大、最强的 Hook(钩子),通俗的讲就是给后续小伙伴开发组件,提供一个标准公共可拔插”接口”,大家可以理解为手机充电器的工业标准口,为了方便各个充电器生产厂家生产,而制定的标准。

既然说自定义标签如此强大,我们在哪里应用的呢 我们知道 spring 发展越来越强大,但核心的组件只有 spring-core 和 spring-beans,那么有些人会问我们经常用的 context,aop,tx 不是吗?对的,这些都是在核心上做的扩展,而这些扩展恰恰是通过自定义标签实现扩展的,还有一些公共开源组件 amq,redis,dubbo 等等,他们都利用了自定义标签来扩展,使得 spring 容器的功能越发强大。

2.自定义标签使用

既然自定义标签用途那么广,那么如何使用自定义标签呢 这里我将带领大家感受下自定义标签的使用。需要的文件(按照 spring 加载、解析的顺序)有以下五个基本文件:

  1. spring.schemas 2.XSD 文件
  2. spring.handlers
  3. NamespaceHandler
  4. BeanDefinitionParser
  5. 最后引入到配置文件中使用。

基本自定义标签需要实现两个关键接口:NamespaceHandlerSupport,BeanDefinitionParser

3.实例

3.1 META-INF 目录下新建 spring.schemas 文件

内容如下

http\://www.gameboys.cn/schema/diy/diy.xsd=META-INF/diy.xsd

3.2META-INF 目录下新建 diy.xsd 文件

内容如下

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.gameboys.cn/schema/diy"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 targetNamespace="http://www.gameboys.cn/schema/diy"
 elementFormDefault="qualified">

 <xsd:import namespace="http://www.w3.org/XML/1998/namespace"></xsd:import>

 <xsd:element name="user">
  <xsd:complexType>
   <xsd:attribute name="id" type="xsd:string"></xsd:attribute>
   <xsd:attribute name="name" type="xsd:string"></xsd:attribute>
   <xsd:attribute name="password" type="xsd:string"></xsd:attribute>
   <xsd:attribute name="phone" type="xsd:string"></xsd:attribute>
  </xsd:complexType>
 </xsd:element>

</xsd:schema>

3.3META-INF 目录下新建 spring.handlers 文件

内容如下 http\://www.gameboys.cn/schema/diy=cn.gameboys.diy.ioc.DiyHandlers

3.4 编写 DiyHandlers

package cn.gameboys.diy.ioc;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class DiyHandlers extends NamespaceHandlerSupport {

 @Override
 public void init() {
  registerBeanDefinitionParser("user", new UserParserDefinitions());
 }
}


3.5 编写 UserParserDefinitons

package cn.gameboys.diy.ioc;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.w3c.dom.Element;

/**
 * @author Sniper
 * @date 2018年10月10日
 */
public class UserParserDefinitons extends AbstractSingleBeanDefinitionParser{

 @Override
 protected Class<?> getBeanClass(Element element) {
  return UserConfig.class;
 }

 @Override
 protected void doParse(Element element, BeanDefinitionBuilder builder) {
  String name = element.getAttribute("name");
  String password = element.getAttribute("password");
  String phone = element.getAttribute("phone");
  builder.addPropertyValue("name", name);
  builder.addPropertyValue("password", password);
  builder.addPropertyValue("phone", phone);
 }


}


3.6 编写 bean UserConfig

package cn.gameboys.diy.ioc;

import java.io.Serializable;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

public class UserConfig implements Serializable, BeanFactoryAware {
 private static final long serialVersionUID = -5680714598279222721L;

 private String id;

 private String name;

 private String password;

 private String phone;

 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 getPassword() {
  return password;
 }

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

 public String getPhone() {
  return phone;
 }

 public void setPhone(String phone) {
  this.phone = phone;
 }

 @Override
 public String toString() {
  StringBuffer sb = new StringBuffer();
  sb.append("name=" + name).append(" password=" + password).append(" phone=" + phone).append(" id").append(id);
  return sb.toString();
 }

 BeanFactory beanFactory;

 @Override
 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  this.beanFactory = beanFactory;
  System.out.println("setBeanFactory");
 }
}



3.7 编写测试类



package cn.gameboys.diy.ioc;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *
* @Description:
* @author: sniper(1084038709@qq.com)
* @date:2020年6月22日 上午11:11:49
 */
public class MainTest {
 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:diy-config.xml");
  UserConfig userconfig = (UserConfig) context.getBean("testDiy");
  System.out.println(userconfig);
  //org.springframework.aop.config.AopNamespaceHandler cn.gameboys.diy.ioc.DiyHandlers
 }
}

3.8 配置 xml diy-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:diy="http://www.gameboys.cn/schema/diy"
 xsi:schemaLocation="http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.gameboys.cn/schema/diy
        http://www.gameboys.cn/schema/diy/diy.xsd"
>


 <context:annotation-config />
 <context:component-scan base-package="cn.gameboys.diy.ioc"></context:component-scan>


 <diy:user id="testDiy" name="sniper" password="123456" phone="110"></diy:user>
</beans>


4.总结

自定义 spring 标签其实只是做了 IOC 的BeanDefinition 的 Resource 定位、BeanDefinition 的载入和解析、BeanDefinition 的注册,通过NamespaceHandlerSupport接口将配置文件 xml 装载为 Resource 对象,然后通过BeanDefinitionParser接口将 resource 解析成一个 BeanDefinition 对象,然后存储到 BeanDefinitionRegistry 中

正文到此结束