Spring框架中自定义XML标签及解析过程分析
要实现自定义的xml
配置,需要有两个默认spring
配置文件来支持。一个是spring.schemas
,一个是spring.handlers
,前者是为了验证你自定义的xml
配置文件是否符合你的格式要求,后者是告诉spring
该如何来解析你自定义的配置文件。
自定义标签涉及的核心接口为:
NamespaceHandler
——命名空间处理器-
BeanDefinitionParser
——BeanDefinition
解析器 实际使用的时候,一般分别继承类: NamespaceHandlerSupport:init()
AbstractBeanDefinitionParser:parse()
以spring
事务标签为例(前提:要了解bean
注册过程):
一、配置文件
spring.handlers
spring.schemas
二、调用逻辑
org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
org.springframework.context.support.AbstractApplicationContext#refreshBeanFactory
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
org.springframework.context.support.AbstractRefreshableApplicationContext#loadBeanDefinitions
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
- 在第3步中
registerBeanDefinitions
调用createReaderContext
// class XmlBeanDefinitionReader
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
- 第4步
createReaderContext
中调用getNamespaceHandlerResolver
方法获取:NamespaceHandlerResolver
// class XmlBeanDefinitionReader
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
new
一个 DefaultNamespaceHandlerResolver
// class XmlBeanDefinitionReader
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
DEFAULT_HANDLER_MAPPINGS_LOCATION: META-INF/spring.handlers
// DefaultNamespaceHandlerResolver
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation; // 重要,命名空间处理器位置
}
DefaultNamespaceHandlerResolver
的属性:private volatile Map handlerMappings
保存的是
namespace URI
与 NamespaceHandler
的映射
- 第5步调用
DefaultBeanDefinitionDocumentReader
(父类BeanDefinitionDocumentReader
)的registerBeanDefinitions
方法
// BeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
delegate.parseCustomElement(ele);
这一行代码是解析自定义标签
// class DefaultBeanDefinitionDocumentReader
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
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); // 解析自定义标签
}
}
- 根据元素标签获取:
NamespaceHandler
,并调用其init
方法 - 调用
NamespaceHandler
的parse
方法,在parse
方法里解析标签,然后返回BeanDefinition
,后续会被spring
注册
// class BeanDefinitionParserDelegate
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
三、定制点
- 定制
init
方法:一般在init
方法中注册BeanDefinitionParser
, 存储在其父类NamespaceHandlerSupport
的属性:parsers
中
- 定制
parse
方法:一般在parse
方法中返回BeanDefinition