xml解析之自定义标签解析

# 自定义标签解析入口

  1. 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));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. 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;
}
1
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;
}
1
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());
}
1
2
3
4

XmlBeanDefinitionReader#getNamespaceHandlerResolver

public NamespaceHandlerResolver getNamespaceHandlerResolver() {
	if (this.namespaceHandlerResolver == null) {
		this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
	}
	return this.namespaceHandlerResolver;
}
1
2
3
4
5
6

如果为空则创建默认解析器,当然也可以在创建XmlBeanDefinitionReader时自定义解析器。

XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver

protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
	ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
	return new DefaultNamespaceHandlerResolver(cl);
}
1
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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 自定义标签处理器

回到获取自定义标签解析器的地方

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
1

弄懂了解析器的来源,接下来看它的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;
		}
		//...
	}
}
1
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;
}
1
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>
1
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
1
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());
	}

}
1
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);
}
1
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);
}
1
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;
}
1
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);
}
1
2
3
4
5
6
7
8
9
10
11
12

decorators也是在init方法中注册,之后根据标签名选择具体的装饰器。

上次更新: 2023/04/09, 16:34:32
最近更新
01
go-admin-ui项目仿写练手1-登录页
06-29
02
maven依赖问题
06-17
03
JVM相关命令
02-21
更多文章>