概述
XML格式中,标签是可以带命名空间的。命名空间主要是解决标签命名冲突的;可能某个标签名在同一个XML中代表两种意思,这是就需要使用命名空间来区分这两种不同的用途。详细的可以去百度XML命名空间。
spring的解析XML时,会分两种情况,一种是默认命名空间标签,spring会正常当做bean来解析;还有一种是带命名空间的标签,比如:
- <tx:annotation-driven transaction-manager=“transactionManager”></tx:annotation-driven>
- <aop:config>
这种带命名空间的标签会有专门的解析器,也可以按照spring 的机制,自定义一个带命名空间的标签,来实现特殊的配置。
带命名空间的标签的解析过程
带命名空间的标签,spring代码中称为(custom tag),即自定义标签。
- 走读代码会发现,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);
}
}
- 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 来进行处理的。
- NamespaceHandler是如何注册进去的?
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包实现了事务管理器,他可以解析<tx:annotation-driven transaction-manager=“transactionManager”>标签