spring扩展-自定义命名空间

概述

XML格式中,标签是可以带命名空间的。命名空间主要是解决标签命名冲突的;可能某个标签名在同一个XML中代表两种意思,这是就需要使用命名空间来区分这两种不同的用途。详细的可以去百度XML命名空间。
spring的解析XML时,会分两种情况,一种是默认命名空间标签,spring会正常当做bean来解析;还有一种是带命名空间的标签,比如:


  • 这种带命名空间的标签会有专门的解析器,也可以按照spring 的机制,自定义一个带命名空间的标签,来实现特殊的配置。

带命名空间的标签的解析过程

带命名空间的标签,spring代码中称为(custom tag),即自定义标签。

  1. 走读代码会发现,XML格式配置解析最终会走到
    DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
    下面是相关代码
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	# 判断该标签是不是默认命名空间,root是<beans>标签
	if (delegate.isDefaultNamespace(root)) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
					# 解析默认命名空间的标签
					parseDefaultElement(ele, delegate);
				}
				else {
				    # 解析自定义标签
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}
  1. delegate.parseCustomElement就是BeanDefinitionParserDelegate#parseCustomElement方法,代码如下:
# 代码有删减
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	String namespaceUri = getNamespaceURI(ele);
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

从代码中可以看到,每个不同命名空间都有自己的NamespaceHandler 来进行处理的。

  1. NamespaceHandler是如何注册进去的?
    spring扩展-自定义命名空间_第1张图片

xml格式的在加载bean的过程中,会一层层委托下去,最终由BeanDefinitionParserDelegate解析XML生成bean的定义(BeanDefinition)。

2中的代码this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
getNamespaceHandlerResolver最终会获取到DefaultNamespaceHandlerResolver,这个是在XmlBeanDefinitionReader中创建的。
下面贴出DefaultNamespaceHandlerResolver主要的代码:

# 代码有删减
public NamespaceHandler resolve(String namespaceUri) {
	Map<String, Object> handlerMappings = getHandlerMappings();
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	String className = (String) handlerOrClassName;
	Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
	NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
	namespaceHandler.init();
	return namespaceHandler;
}

private Map<String, Object> getHandlerMappings() {
	# 代码有删减
	# this.handlerMappingsLocation的默认路径为"META-INF/spring.handlers"
	Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
	handlerMappings = new ConcurrentHashMap<>(mappings.size());
    return handlerMappings;
}

从代码中可以看到,自定义标签的handler是配置在META-INF/spring.handlers中的,跟spring的SPI机制很类似。
这样根据这个机制,我们也可以自定义一个标签,并实现对应的NamespaceHandler,配置好spring.handlers文件,就可以实现我们自己的扩展。
我们可以去看一下,spring-tx-xx.jar,这个jar包实现了事务管理器,他可以解析标签

spring扩展-自定义命名空间_第2张图片

你可能感兴趣的:(spring)