5.Spring 如何解析自定义命名空间

说明

        我们在 Spring 的 xml 配置文件里经常定义各种各样的配置(tx、mvc、context等),我们将它叫做 Namespace,即:命名空间。当使用 Spring 集成第三方框架时,也会看到一些 Spring 之外的配置,例如 Dubbo 的配置、security 的配置、redis 的配置等等。Spring 作为一个庞大的容器,到底是怎么工作的呢?这篇文章将简单的介绍一下其实现机制,具体细节朋友们可以自己查看相关代码。

<!-- 我们把这些都叫做Namespace,即:命名空间 -->
<!-- tx -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">

</tx:advice>

<!-- mvc -->
<mvc:annotation-driven />

<!-- context -->
<context:component-scan base-package="com.mvc"/>

<!-- dubbo -->
<dubbo:application name="hello-world-app"  />

1.Spring 何时开始解析自定义命名空间配置

        分析过 Spring 源码的朋友都知道 Spring 的入口方法就是 refresh(),不了解可点击链接了解:Spring IOC 源码解析。此处附上Spring IOC 源码分析时序图,我们就不再介绍 Spring 容器启动的流程,直接从 delegate.parseCustomElement(ele);这个方法入手。在这里插入图片描述

2.解析自定义命名空间配置源码分析

  2.1 parseCustomElement() 方法
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//1.getNamespaceURI()
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	//2.resolve()方法解析
	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));
}

        根据元素的 namespaceURI(也就是 xml 文件头那一串schema)找到对应的 NamespaceHandler.【这里就是扩展自定义配置的入口】

  2.2 我们接下来分析 resolve() 方法
/**
 * Locate the {@link NamespaceHandler} for the supplied namespace URI
 * from the configured mappings.
 * @param namespaceUri the relevant namespace URI
 * @return the located {@link NamespaceHandler}, or {@code null} if none found
 */
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
	//3.通过 getHandlerMappings() 获取【命名空间与解析命名空间类】匹配的对应关系
	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;
		}
		catch (ClassNotFoundException ex) {
			throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
					namespaceUri + "] not found", ex);
		}
		catch (LinkageError err) {
			throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
					namespaceUri + "]: problem with handler class file or dependent class", err);
		}
	}
}

        对应的 NamespaceHandler 就是根据 handlerMappings 找到匹配关系的,所以我们只需要看DefaultNamespaceHandlerResolver.getHandlerMappings()即可。

  4.getHandlerMappings()方法分析
/**
 * Load the specified NamespaceHandler mappings lazily.
 */
private Map<String, Object> getHandlerMappings() {
	Map<String, Object> handlerMappings = this.handlerMappings;
	if (handlerMappings == null) {
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			if (handlerMappings == null) {
				try {
					//此处 handlerMappingsLocation,Spring 固定路径为:"META-INF/spring.handlers"
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isDebugEnabled()) {
						logger.debug("Loaded NamespaceHandler mappings: " + mappings);
					}
					Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
					CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
					handlerMappings = mappingsToUse;
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return handlerMappings;
}
/**
 * The location to look for the mapping files. Can be present in multiple JAR files.
 */
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

        Spring 会将 classpath 下的 META-INF/spring.handlers中的配置,都加载到 handlerMappings 中。这个spring.handler文件就是以 NamespaceUrl 作为key,对应的Handler作为 value 的键值对。如下图罗列所示:
在这里插入图片描述
        Spring 容器启动,当解析到<tx:advice>配置的时候,它便会根据我们在 xml 中配置的 tx 对应的 schema 【xmlns:tx="http://www.springframework.org/schema/tx"】作为key,然后找到对应的 TxNamespaceHandler 类进行配置解析。

        Spring 就是通过这种方式,来解析三方技术自定义的命名空间,从而可以完美整合兼容所有的技术。膜拜开发 Spring 的大佬们,向你们学习,加油。

3.自定义命名空间

        Spring 中自定义命名空间,请跳转链接查看:Spring 自定义命名空间


Spring 如何解析自定义命名空间,介绍到此为止

如果本文对你有所帮助,那就给我点个赞呗 ^ _ ^

End

发布了301 篇原创文章 · 获赞 66 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/lzb348110175/article/details/104861610