【Spring】自定义XML标签custorm:user

目录

1.前言

2.Spring自定义标签

2.1 开发步骤

2.2 编码实践demo

3.源码解析

3.1 加载xsd文件

3.2 加载自定义NamespaceHandler

3.3 XmlBeanDefinitionReader解读BeanDefinition


1.前言

spring提供了接口可以供我们自定义xml配置标签,如它自身的aop空间config标签,Spring是如何解析它们的?我们如何使用Spring提供的机制自定义xmlns命令空间和标签,且能被spring解析呢?

2.Spring自定义标签

2.1 开发步骤

  1. 设计配置属性和JavaBean
  2. XSD文件
  3. BeanDefinitionParser标签解析类
  4. 标签解析类的NamespaceHandler类
  5. spring.handlers和spring.schemas供Spring读取
  6. 在Spring中使用

2.2 编码实践demo

1.javaBean

public class User {
    private String userName;
    private String email;
// 省略getter setter toString
}

2.xsd定义标签模式

xml schema definition文件,用于定义自定义标签的层次结构模式,放置在项目的META-INF/user.xsd,定义user标签

<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.wy.com/schema/user"
        xmlns:tns="http://www.wy.com/schema/user"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
            <attribute name="id" type="string" />
            <attribute name="userName" type="string" />
            <attribute name="email" type="string" />
        </complexType>
    </element>
</schema>

3. 定义解析类

解析xml配置,注入到java bean的属性

public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    @Override
    protected Class getBeanClass(Element element) {
        return User.class;
    }
    @Override
    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        String userName = element.getAttribute("userName");
        String email = element.getAttribute("email");

        if (StringUtils.hasText(userName)) {
            bean.addPropertyValue("userName", userName);
        }
        if (StringUtils.hasText(email)) {
            bean.addPropertyValue("email", email);
        }
    }
}

4. NamespaceHandler命名空间处理

为防止一个标签重复起冲突,一般都会用命令空间隔离,如<aop:config>标签中aop就是命名空间,config是一个标签,命令空间中可以有很多标签,每个标签都有对应的parser解析类,一个命名空间有一个命名空间处理器。

自定义命名空间custom,重写init方法,将user标签的解析类注册进处理器

public class CustomNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        // 关联元素名 和解析类
        registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
    }
}

5.spring.handlersspring.schemas供spring读取

#spring.schemas 注册自定义的标签xsd
http\://www.wy.com/schema/user.xsd=META-INF/user.xsd

#spring.handlers 注册命名空间处理器
http\://www.wy.com/schema/user=framework.spring.customXmlElement.CustomNamespaceHandler

6.在spring使用

直接启动spring容器,spring就可以解析到自定义的标签,并生产出BeanDefinition

<?xml version="1.0" encoding="UTF-8"?>
<beans default-autowire="byName" xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:custorm="http://www.wy.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.wy.com/schema/user
                           http://www.wy.com/schema/user.xsd">

    <custorm:user id="user" userName="张三" email="[email protected]" />
</beans>
public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("framework/springCustomElement.xml");
        User user = (User)applicationContext.getBean("user");
        System.out.println(user);
    }
}

执行结果:

User{userName='张三', email='[email protected]'} 

3.源码解析

3.1 加载xsd文件

Spring的PluggableSchemaResolver会根据META-INF/目录下的spring.schemas文件,配置了标签的模式xsd文件路径

3.2 加载自定义NamespaceHandler

DefaultNamespaceHandlerResolver解析spring.handlers到map,拿到命名空间处理器,并调用其init方法,注册解析类

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

private Map<String, Object> getHandlerMappings() {
	if (this.handlerMappings == null) {
		synchronized (this) {
			if (this.handlerMappings == null) {
				try {
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isDebugEnabled()) {
						logger.debug("Loaded NamespaceHandler mappings: " + mappings);
					}
					Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return this.handlerMappings;
}

3.3 XmlBeanDefinitionReader解读BeanDefinition

在容器启动,加载beanDefinition时会有一步解析自定义元素,这一步会使用上一个步骤配置的命令空间处理器NamespaceHandler去解析标签元素,NamespaceHandler会在自己的parsers map中查找标签对应的解析器进行解析。

发布了92 篇原创文章 · 获赞 14 · 访问量 5835

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/103416978