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的过程。到这里自定义标签的解析就完成了。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值