xml解析之自定义标签解析
# 自定义标签解析入口
- beans标签下
在通过xml文件加载BeanDefinition (opens new window)的最后引出了xml解析自定义标签
BeanDefinitionParserDelegate#parseCustomElement
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//获取命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//获取对应的自定义标签处理器
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));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- bean标签下 在xml解析之默认标签解析 (opens new window)的最后介绍了bean标签中也是用到自定义标签 decorateBeanDefinitionIfRequired方法调用到decorateIfRequired方法
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
//获取命名空间
String namespaceUri = getNamespaceURI(node);
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
//不是默认标签,寻找标签处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
//出现了无法处理的spring默认标签,报错
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
// 没有找到自定义标签的处理器
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 自定义标签解析器的配置
从readerContext.getNamespaceHandlerResolver()
获取自定义标签解析器,由它寻找相应的处理器
XmlReaderContext.NamespaceHandlerResolver
有构造方法传入,向上追溯
public XmlReaderContext(
Resource resource, ProblemReporter problemReporter,
ReaderEventListener eventListener, SourceExtractor sourceExtractor,
XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
super(resource, problemReporter, eventListener, sourceExtractor);
this.reader = reader;
this.namespaceHandlerResolver = namespaceHandlerResolver;
}
2
3
4
5
6
7
8
9
该构造方法在XmlBeanDefinitionReader中调用
XmlBeanDefinitionReader#createReaderContext
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
2
3
4
XmlBeanDefinitionReader#getNamespaceHandlerResolver
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
2
3
4
5
6
如果为空则创建默认解析器,当然也可以在创建XmlBeanDefinitionReader时自定义解析器。
XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
2
3
4
进入DefaultNamespaceHandlerResolver
//自定义标签处理器的配置文件路径
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自定义标签处理器
回到获取自定义标签解析器的地方
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
弄懂了解析器的来源,接下来看它的resolve方法如何选择相应的自定义标签处理器
DefaultNamespaceHandlerResolver
public NamespaceHandler resolve(String namespaceUri) {
//获取处理器映射 保存从配置中读取的处理器类名或已经实例化的处理器类
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
//反射 实例化
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
//...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
try {
//读取META-INF/spring.handlers路径下的配置
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(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 handlerMappings;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# context:component-scan标签
下面以context:component-scan
为例体会Spring是如何使用自定义标签的。
该标签用于配置要扫描的包路径,以及包括或排除的类。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在spring-context
项目/resources/META-INF/
找到spring.handlers
文件
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
2
3
4
5
命名空间http\://www.springframework.org/schema/context
对应的处理器是ContextNamespaceHandler
ContextNamespaceHandler
的UML类图
当需要解析context:component-scan
标签时,首先获得对应的命名空间http\://www.springframework.org/schema/context
,由命名空间找到对应的处理器ContextNamespaceHandler
,将其实例化,然后调用init
方法
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NamespaceHandlerSupport#registerBeanDefinitionParser
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
2
3
4
5
所以init
方法实际上是将命名空间http\://www.springframework.org/schema/context
下的所有标签的解析类保存到ContextNamespaceHandler
。
# beans标签下的自定义标签
回顾自定义标签解析入口处的代码,在拿到自定义标签处理器ContextNamespaceHandler
后,调用parse
方法开始解析标签。
实际调用的是NamespaceHandlerSupport#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
//寻找对应的标签解析器,之前init方法已缓存
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
2
3
4
5
ComponentScanBeanDefinitionParser#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
//获取base-package的值
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
//替换占位符为具体路径
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
//支持配置多个路径,分隔符",; \t\n"
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
//配置扫描器
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//扫描包路径下的bean
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//注册扫描到的BeanDefinition
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# bean标签下的自定义标签
在拿到自定义标签处理器NamespaceHandler
后,调用decorate
方法开始解析标签。
实际调用的是NamespaceHandlerSupport#decorate
private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
public BeanDefinitionHolder decorate(
Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
return (decorator != null ? decorator.decorate(node, definition, parserContext) : null);
}
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
this.decorators.put(elementName, dec);
}
2
3
4
5
6
7
8
9
10
11
12
decorators
也是在init
方法中注册,之后根据标签名选择具体的装饰器。