天天看點

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的過程。到這裡自定義标簽的解析就完成了。