《Spring源码深度解析》阅读笔记6-自定义标签的解析

在之前的章节中,我们提到了Spring中存在默认与自定义两种标签,在上一章节中我们分析了Spring对默认标签的解析过程,所以在这一章节将分析Spring对自定义标签的解析过程。我们回顾一下,当完成从配置文件到Document的转换并提取对应的root后,开始对所有元素的解析,而这一过程中便开始了默认标签与自定义标签两种格式的区分,函数如下:
在本章中,所有的功能都围绕其中一句代码delegate.parseCustomElement(root)开展的。在分析自定义标签解析过程之气那,我们先了解一下自定义标签的使用过程。

自定义标签使用
Spring提供了可扩展Schema的支持,扩展Spring自定义标签大致需要以下几个步骤(前提是要把Spring的Core包加入项目中)。
(1)创建一个需要扩展的组件;
(2)定义一个XSD文件描述组件内容;
(3)创建一个文件,实现BeanDefinitionParse接口,用来解析XSD文件中的定义和组件定义;
(4)创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器;
(5)编写Spring.handlers和Spring.schemas文件。
详细实例: http://blog.csdn.net/nangongyanya/article/details/53905671

自定义标签的解析
相信了解了自定义标签的使用方法后,或多或少会对自定义标签的实现过程有一个自己的想法。其实思路非常简单,无非是根据对应的bean获取对应的命名空间,根据命名空间解析对应的处理器,然后根据用户自定义的处理器进行解析。源代码如下:
public BeanDefinition parseCustomElement(Element ele) {
		return parseCustomElement(ele, null);
	}
	
	// containingBd为父类bean,对顶层元素的解析应设置为null
	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;
		}
		// 调用自定义的NamespaceHandler进行解析
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

1、获取标签的命名空间
无论是区分Spring中默认标签和自定义标签还是区分自定义标签中不同标签的处理器都是以标签所提供的命名空间为基础,而至于如何提取对应元素的命名空间其实并不需要我们亲自去实现,在org.w3c.dom.Node中已经提供了方法让我们直接调用:
2、 提取自定义标签处理器
有了命名空间就可以进行NamespaceHandler的提取了,继续之前parseCustomElement函数的跟踪,分析NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri),在readerContext初始化的时候其属性namespaceHandlerResolver已经被初始化为DefaultNamespaceHandlerResolver的实例,所以这里调用的resolve(namespaceUri)是DefaultNamespaceHandlerResolver的方法。

/**
	 * 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</code> if none found
	 */
	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的初始化方法
				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的过程,通过之前的示例程序我们了解到如果使用自定义标签,那么其中必不可少的操作就是在Spring.hendlers文件中配置命名空间与命名空间处理器的映射关系。只有这样Spring才能根据映射关系找到匹配的处理器,而寻找匹配的处理器就是上面函数中所实现的,当获取到自定义的NamespaceHandler之后就可以进行处理器初始化并解析了。
3、标签解析
得到解析其以及要分析的元素后,Spring就可以将解析工作委托给自定义解析器去解析了。程序首先获取在MyNamespaceHandler类中的init方法中注册对应的UserBeanDefinitionParser实例,并调用其parse方法进行进一步解析。

回顾一下全部的自定义标签处理过程,虽然在实例中我们定义UserBeanDefinitionParser,但是在其中我们只是做了与自己业务逻辑相关的部分。不过我们没做并不代表没有, 在这个过程中同样也是按照Spring中默认标签的处理方式进行,包括创建BeanDefinition以及进行相应默认属性的设置,对于这些工作Spring都默默地帮我们实现了,只是暴露出一些接口来供用户实现个性化业务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值