spring自定义xml标签
要实现自定义的xml配置,需要有两个默认spring配置文件来支持。一个是spring.schemas,一个是spring.handlers,前者是为了验证你自定义的xml配置文件是否符合你的格式要求,后者是告诉spring该如何来解析你自定义的配置文件。
自定义标签涉及的核心接口为:
- NamespaceHandler
- BeanDefinitionParser
实际使用的时候,一般分别继承类:
- NamespaceHandlerSupport:init
- AbstractSingleBeanDefinitionParser:parse
以spring事务标签为例(前提:要了解bean注册过程):
1. 配置文件:
spring.handlers
spring.schemas
2. 调用逻辑
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));
}
3. 定制点
- 定制 init 方法:一般在init方法中注册BeanDefinitionParser, 存储在其父类NamespaceHandlerSupport的属性:parsers 中
- 定制 parse 方法:一般在parse方法中返回BeanDefinition