Spring 源码阅读-2-自定义标签简单使用与解析

自定义标签解析1、自定义标签的使用在探究自定义标签的解析过程前,温顾一下使用会更方便解析过程的理解 。
定义XSD文件
【Spring 源码阅读-2-自定义标签简单使用与解析】<?xml version="1.0"?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"targetNamespace="http://www.w3school.com.cn"xmlns="http://www.w3school.com.cn"elementFormDefault="qualified"> <element name="user"><complexType><attribute name="id" type="String"></attribute><attribute name="userName" type="String"></attribute><attribute name="email" type="String"></attribute></complexType></element></xs:schema>XSD 如何定义可以看w3c中XML Schema中的教程
定义实体类
public class User{ private String id; private String userName; private String email; // 省略 set/get 方法}实现BeanDefinitionParser接口
/** * 自定义标签的解析器 */public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {@Overrideprotected Class<?> getBeanClass(Element element) {return User.class;}/*** 用来解析自定义的标签*/@Overrideprotected void doParse(Element element, BeanDefinitionBuilder bean) {// 获取自定义标签属性String userName = element.getAttribute("userName");String email = element.getAttribute("email");bean.addPropertyValue("userName",userName);bean.addPropertyValue("email",email);}}扩展NamespaceHandlerSupport
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;public class CustomNamespaceHandlerSupport extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("user", new UserBeanDefinitionParser());}}配置此代码后,当遇到自定义标签user:xxxx,user开头的自定义标签的元素时,就会使用UserBeanDefinitionParser解析器解析 。
配置schema文件路径以及handler
默认在META-INF文件夹下配置
Spring.handlers
http://www.w3school.com.cn/schema/user=com.yxx.CustomNamespaceHandlerSupportSpring.schemes
http://www.w3school.com.cn/schema/user.xsd=META-INF/Spring-test.xsd配置此信息可以让Spring找到xsd文件以及自定义NamespaceHandlerSupport
2、自定义标签解析回到在默认标签解析时候的方法
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 对beans 处理if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {// 对 bean处理Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {// 处理 默认的 http //w ww.springframework org/scherna beansparseDefaultElement(ele, delegate);}else {// 处理自定义的 比如 tx下的delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}解析自定义标签
BeanDefinitionParserDelegate#parseCustomElement
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {// 获取 命名空间 namespaceUri http://xxxxx.xsd// w3c提供的Node 提供了方法String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;}// 提取自定义的标签处理器// 通过命名空间获取对应的namespacehandlerNamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}// 使用自定自定义的 namespacehandler 进行解析// 例如 <myname:user>namespacehandler 可以解析全部的myname 命名空间的 标签// 命名空间处理器namespacehandler 通过 不同的标签调用不同的AbstractSingleBeanDefitionParserreturn handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}此方法中首先获取此ele的命名空间,并通过命名空间处理程序解析器(DefaultNamespaceHandlerResolver)根绝命名空间名称获取命名空间处理器(NamespaceHandler),然后通过parse方法处理自定义标签(实际上此方法执行的就是UserBeanDefinitionParser#doParse) 。
DefaultNamespaceHandlerResolver#resolve
public NamespaceHandler resolve(String namespaceUri) {// 获取handler mapper -----> 其实就是Spring.handlers 中配置的namespace-handlerMap<String, Object> handlerMappings = getHandlerMappings();// 查看此namespace是否存在对应的handlerObject handlerOrClassName = handlerMappings.get(namespaceUri);// 不存在if (handlerOrClassName == null) {return null;}// 如果存在缓存 直接返回else if (handlerOrClassName instanceof NamespaceHandler) {return (NamespaceHandler) handlerOrClassName;}else {// 存的是classNameString className = (String) handlerOrClassName;try {// 获取对应的classname 的Class对象Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);// 判断此Class 是否 集成实现 NamespaceHandlerif (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");}// 实例化 namespacehandler 并类型转换NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);// 初始化// 将AbstractSingleBeanDefitionParser实例化并 注册到BeanDefnitionParser缓存中, registerBeanDefnitionParsernamespaceHandler.init();// 将namespacehandler添加到缓存handlerMappings.put(namespaceUri, namespaceHandler);return namespaceHandler;}...}}handlerMappings此Map缓存中存放的为namespace-handler的映射,但是这个handler可能是已经实例化的NamespaceHandler,也可能是handlerClassName,如果是handlerClassName,需要先实例化再添加到缓存
NamespaceHandlerSupport#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {// 查找 parserContext 对应的 的解析器BeanDefinitionParser parser = findParserForElement(element, parserContext);// parser不为空 就解析标签, 并将beanDefinition注册到Spring容器中return (parser != null ? parser.parse(element, parserContext) : null);}将任务分解,首先查找出该ele解析的BeanDefinitionParser,然后进行解析,最后会执行UserBeanDefinitionParser#doParse 。
NamespaceHandlerSupport#findParserForElement
@Nullableprivate BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {// 获取<myname:user>中的userString localName = parserContext.getDelegate().getLocalName(element);// registerBeanDefinitionParser (”user ”, new UserBeanDefinitionParser ()) ;这里已经将 user --解析器保存在了map中// 获取user对应的解析器BeanDefinitionParser parser = this.parsers.get(localName);if (parser == null) {parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);}return parser;}解析完成
AbstractBeanDefinitionParser#parse
public final BeanDefinition parse(Element element, ParserContext parserContext) {// 解析自定义标签除去默认属性的其他属性以及子标签AbstractBeanDefinition definition = parseInternal(element, parserContext);if (definition != null && !parserContext.isNested()) {try {String id = resolveId(element, definition, parserContext);if (!StringUtils.hasText(id)) {parserContext.getReaderContext().error("Id is required for element '" + parserContext.getDelegate().getLocalName(element)+ "' when used as a top-level tag", element);}String[] aliases = null;// 解析 name 分割成别名if (shouldParseNameAsAliases()) {String name = element.getAttribute(NAME_ATTRIBUTE);if (StringUtils.hasLength(name)) {aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));}}// 构造BeanDefinitionHolder对象BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);// 注册beanDefinitionregisterBeanDefinition(holder, parserContext.getRegistry());if (shouldFireEvents()) {BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);postProcessComponentDefinition(componentDefinition);parserContext.registerComponent(componentDefinition);}}catch (BeanDefinitionStoreException ex) {String msg = ex.getMessage();parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);return null;}}return definition; }AbstractSingleBeanDefinitionParser#parseInternal
@Overrideprotected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {// 创建 BeanDefinition 构造器BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();// getParentName 由子类去实现获取父类名字String parentName = getParentName(element);if (parentName != null) {builder.getRawBeanDefinition().setParentName(parentName);}// getBeanClass 由子类重写获取当前标签对应的类类型 返回ClassClass<?> beanClass = getBeanClass(element);if (beanClass != null) {builder.getRawBeanDefinition().setBeanClass(beanClass);}else {// getBeanClassName 由子类重写获取当前标签对应的类类型 字符串String beanClassName = getBeanClassName(element);if (beanClassName != null) {builder.getRawBeanDefinition().setBeanClassName(beanClassName);}}builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));BeanDefinition containingBd = parserContext.getContainingBeanDefinition();// 存在父类if (containingBd != null) {// Inner bean definition must receive same scope as containing bean.builder.setScope(containingBd.getScope());}if (parserContext.isDefaultLazyInit()) {// Default-lazy-init applies to custom bean definitions as well.builder.setLazyInit(true);}// 子类重写的 解析方法doParse(element, parserContext, builder);return builder.getBeanDefinition();}此方法解析parent、beanClass以及继承父类的scope和是否懒加载,并最终调用doParse方法实现自定义标签的处理,doParse此方法就为UserBeanDefinitionParser#doParse
至此自定义标签的解析完毕
经历的过程:

  1. 通过命名空间获取自定义的标签处理器
  2. 通过element获取当前标签的解析器
  3. 处理默认属性id、name以及name分割后的alias
  4. 集成父类的scope和是否懒加载
  5. 调用自定义处理标签的UserBeanDefinitionParser#doParse方法
  6. 构造BeanDefinitionHolder对象
  7. 注册BeanDefinition
---- ** Cover 《Spring 源码深度解析》**