05-spring源码分析-Spring如何实现自定义命名空间

2019-02-01  本文已影响0人  cjxz

上一篇文章我们使用spring提供的NamespaceHandlerSupportBeanDefinitionParser自己实现了一个自定义命名空间。spring自定义命名空间demo
这一节我们来分析一下spring是如何实现这个逻辑的。

其实我们可以想一下spring提供了各种命名空间,相对的每个命名空间肯定也是通过上面两个抽象类来帮助我们实现的。下面是spring自定义的几个命名空间。可以看到她们的处理。随便看一个

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

下面是UtilNamespaceHandler的部分源码

public class UtilNamespaceHandler extends NamespaceHandlerSupport {

    private static final String SCOPE_ATTRIBUTE = "scope";


    @Override
    public void init() {
        registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
        registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
        registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
        registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
        registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
        registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
    }
//....省略部分代码....

我们使用xsd文件的作用其实是限制在xml里面出现的Element能够有的属性,然后将这个属性映射到一个配置类上。比如我们后面将分析dubbo里面服务端实现原理就用到我们这节。好了我们不扯远了,现在再来看看服务器实现原理。
我们知道spring IOC容器加载的过程是从refresh我们自定义的命名空间也是放到spring的配置文件中,所以自定命名空间也是交给refresh方法调用执行。中间的调用过程我就省略调,直接进入处理的地方DefaultBeanDefinitionDocumentReader下面是主要的调用链,跟着这个调用链就可以直到Spring是如何调用自定义命名空间的

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
  org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
    org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element)
      org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element,         org.springframework.beans.factory.config.BeanDefinition)
         org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve
           org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse
             org.springframework.beans.factory.xml.NamespaceHandlerSupport#findParserForElement
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
                //此处的root是:[beans:null]我们在写spring配置文件时所有的配置都是在<beans>下面
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
    //前置处理,不用管
        preProcessXml(root);
//!!!!这个方法就是自定义命名空间的入口!!!!
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);
        this.delegate = parent;
    }
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//首先判断是否符合beans的命名空间下。我们自定义的命名空间也是写在beans下面,所以会进入if逻辑
        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) {
//这里的node是:myns:mybean
                    Element ele = (Element) node;
//下面会判断element是否是bean,如果不是就走else逻辑,很明显这里Element是myns:mybean所以会进入else逻辑
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//先找到命名空间的url:http://xxx.xxx.com/schema/myns  
        String namespaceUri = getNamespaceURI(ele);
//通过配置文件spring.handlers里面加载处理类
//我们在com.spring.start.namespace.MynsNameSpaceHandler里面重写了init方法。在resolve方法里面会执行init方法
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
//这里执行parse方法,在parse方法里面会调用MybeanParser的parse方法
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return findParserForElement(element, parserContext).parse(element, parserContext);
    }
    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        String localName = parserContext.getDelegate().getLocalName(element);
        BeanDefinitionParser parser = this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        return parser;
    }

上面代码都很简单就不做说明了。
到这里应该可以看到spring是如何加载自定命名空间的。

上一篇下一篇

猜你喜欢

热点阅读