Spring源码解析之自定义标签的解析
阅读须知
- Spring源码版本:4.3.8
- 文章中使用/* */注释的方法会做深入分析
正文
上篇文章我们介绍了Spring默认标签的解析,本文我们来分析一下Spring自定义标签的解析。上篇文章我们了解到Spring的默认标签目前有4个(import、alias、bean、beans),也就是说除了这4个标签以外的标签都是自定义标签(当然这里所说的标签不包括那些以子标签形式存在的如property、value等标签),如我们熟知的事务标签<tx:annotation-driven/>
、注解扫描标签<context:component-scan/>
等都属于自定义标签,下面就让我们来分析一下这些自定义标签是如何解析的,承接Spring源码解析之默认标签的解析文中自定义标签解析的分支:
BeanDefinitionParserDelegate:
public BeanDefinition parseCustomElement(Element ele) {
/* 解析自定义元素 */
return parseCustomElement(ele, null);
}
BeanDefinitionParserDelegate:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
// 获取命名空间
String namespaceUri = getNamespaceURI(ele);
/* 根据命名空间获取NamespaceHandler */
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));
}
DefaultNamespaceHandlerResolver:
public NamespaceHandler resolve(String namespaceUri) {
/* 获取所有已经配置的handler映射 */
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(); // 调用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);
}
}
}
DefaultNamespaceHandlerResolver:
private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
// 加载配置
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(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 this.handlerMappings;
}
这里就是加载配置文件到内存中,而this.handlerMappingsLocation
在调用构造方法初始化时被赋值为META-INF/Spring.handlers,我们来看一下Spring事务自定义标签对应这里的配置,锁定spring-tx包下的META-INF/Spring.handlers:
http\:// www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
所以在解析Spring事务自定义标签时就会找到TxNamespaceHandler并调用其init方法:
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
init方法就是注册对应三个标签的解析器,这里用Spring事务标签举例,其他自定义标签大同小异。下面就是解析过程:
NamespaceHandlerSupport:
public BeanDefinition parse(Element element, ParserContext parserContext) {
/* 寻找解析器进行解析 */
return findParserForElement(element, parserContext).parse(element, parserContext);
}
NamespaceHandlerSupport:
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 以<tx:annotation-driven/>为例,这里的localName就是annotation-driven
String localName = parserContext.getDelegate().getLocalName(element);
// 根据localName获取解析器,上面注册过annotation-driven的解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
获取到BeanDefinitionParser后,调用其parse方法进行解析,解析的过程就是根据开发者需求的不同自行实现了,整体来说就是解析配置到注册BeanDefinition的过程。到这里自定义标签的解析就完成了。
上一篇: 自定义注解+AOP实现对注解的解析实例
下一篇: bean的实例化方式
推荐阅读
-
Vue源码解析之数据响应系统的使用
-
解析在Android中为TextView增加自定义HTML标签的实现方法
-
spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)
-
spring5 源码深度解析----- AOP代理的生成
-
Spring5源码解析4-refresh方法之invokeBeanFactoryPostProcessors
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
-
基于Spring注解的上下文初始化过程源码解析(一)
-
Spring源码解析之ConfigurableApplicationContext
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)