Dubbo|基礎知識之自定義標籤解析流程分析(續)

前面講到利用XML配置文件示範Dubbo的使用,比如服務提供者的provider.xml配置文件內容爲:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo
       http:dubbo.apache.org/schema/dubbo/dubbo.xsd">
  
    <dubbo:application name="test-xml-provider" owner="programmer" organization="dubbox"/>
    <dubbo:registry address="zookeeper://10.19.50.225:2181"/>
    <dubbo:protocol name="dubbo" port="20890" />
    <dubbo:service interface="com.starry.testxmlprovider.service.TestDubbo" ref="dubboService" protocol="dubbo" />
    <bean id="dubboService" class="com.starry.testxmlprovider.service.impl.TestDubboImpl"/>
</beans>

可以看到Dubbo自定義標籤:dubbo:application,dubbo:registry,dubbo:protocol等等,前面我們已經分析過Dubbo標籤的定義,並且說明自定義標籤的使用和解析過程;本篇博文將針對dubbo自定義標籤的解析作一個深入的剖析。

定義好provider.xml配置文件後,通過ClassPathXmlApplicationContext加載xml配置文件。下面給出調用鏈:

org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String)
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

以上調用鏈都是ClassPathXmlApplicationContext的處理過程,爲了控制篇幅這裏就不涉及,下面則開始涉及到dubbo框架的內容。

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 是否爲默認命名空間,默認爲http://www.springframework.org/schema/beans
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        //獲取element結點,即xml的標籤
        for(int i = 0; i < nl.getLength(); ++i) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element)node;
                if (delegate.isDefaultNamespace(ele)) {
                    this.parseDefaultElement(ele, delegate);
                } else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
        // 針對非默認命名空間,則自定義解析元素
        delegate.parseCustomElement(root);
    }

}

除了命名空間爲bean的標籤外,基本都是自定義,類似於AOP,Dubbo等。自定義標籤的解析則委託給BeanDefinitionParserDelegate類解析。

@Nullable
public BeanDefinition parseCustomElement(Element ele) {
    return this.parseCustomElement(ele, (BeanDefinition)null);
}

parseCustomElement(root,null)方法主要完成三件事:

  • 1 根據xml的element獲取對應的xml namespace;如根據dubbo獲取對應的xmlns: http://dubbo.apache.org/schema/dubbo
  • 2 根據xmlns獲取自定義handler;該過程由resolve(xmlns)完成;
  • 3 利用自定義handler解析xml元素;
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    // 根據xml標籤獲取url,如根據dubbo獲取xmlns的值
    String namespaceUri = this.getNamespaceURI(ele);
    if (namespaceUri == null) {
        return null;
    } else {
    // 根據xmlns獲取命名空間handler,如dubbo-DubboNamespaceHandler
    // 獲取handler的過程就會執行上面提到的init()方法
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        // 如果沒有定義namespaceHandler則拋出異常
        if (handler == null) {
            this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        } else {
            // 調用自定義的namespaceHandler處理xml結點
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    }
}

調用鏈路爲:

NamespaceHandlerSupport.parse(Element,ParserContext)

注意解析XML結點後返回的是BeanDefinition對象(實現類RootBeanDefinition的對象).

findParserForElement(Element,ParserContext)

根據該xml結點的localName查找BeanDefinitionParser對象;注意parsers對象,沒錯,它就是DubboNamespaceHandler.init()方法執行時放置name和parsers之間映射關係(map)的對象。所以這裏返回的BeanDefinitionParser對象是DubboBeanDefinitionParser類的對象。

DubboBeanDefinitionParser.parse(Element,ParserContext)

最終調用parse(Element,ParserContext,Class,Boolean),該方法是核心方法所以比較長,下面省略部分並給出關鍵代碼:

private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
    // 聲明返回beanDefinition
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClass(beanClass);
    beanDefinition.setLazyInit(false);

    // 獲取bean的id
    String id = element.getAttribute("id");
    String className;
    int var7;
    if (StringUtils.isEmpty(id) && required) {
    // 如果沒有顯示的配置節點的id,那麼分爲下面幾種情況
    // 1.如果節點配置name屬性,則賦值給id;
    // 2.如果節點沒有配置name屬性,則分下面兩種情況:
    //    2.1 如果此節點是“protocol”,則id賦值爲“dubbo”;
    //    2.2 否則獲取節點中interface屬性的值,若其值爲空則獲取節點對應的bean的name值賦值給id,若有值則直接賦值給id
    }

    if (id != null && id.length() > 0) {
        // 若擁有id重複的RootBeanDefinition,則拋出異常
        if (parserContext.getRegistry().containsBeanDefinition(id)) {
            throw new IllegalStateException("Duplicate spring bean id " + id);
        }
        // 反之id爲key,rootBeanDefinition爲value建立映射關係
        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
        // 把id值寫入rootBeanDefinition的屬性中
        beanDefinition.getPropertyValues().addPropertyValue("id", id);
    }

     // 若該結點的元素爲“protocol”,則…
    if (ProtocolConfig.class.equals(beanClass)) {
        // 爲xml文件中含有protocol屬性的節點生成一個RuntimeBeanReference對象
        String[] var24 = parserContext.getRegistry().getBeanDefinitionNames();
        var7 = var24.length;

        for(int var8 = 0; var8 < var7; ++var8) {
            String name = var24[var8];
            BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
            PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
            if (property != null) {
                Object value = property.getValue();
                if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig)value).getName())) {
                    definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                }
            }
        }
    //若該結點的元素爲“service”,則…
    } else if (ServiceBean.class.equals(beanClass)) {
        // 獲取屬性爲“class”的值,併爲此生成一個新的RootBeanDefinition對象
        // 該新對象以ref爲名放入外圍rootBeanDefinition對象中
        className = element.getAttribute("class");
        if (className != null && className.length() > 0) {
            RootBeanDefinition classDefinition = new RootBeanDefinition();
            classDefinition.setBeanClass(ReflectUtils.forName(className));
            classDefinition.setLazyInit(false);
            parseProperties(element.getChildNodes(), classDefinition);
            beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
        }
    //若該結點的元素爲“provider”,則…
    } else if (ProviderConfig.class.equals(beanClass)) {
        // 針對該結點“service”屬性,迭代生成新的subBeanDefinition
        // subBeanDefinition的provider值爲RuntimeBeanReference(id),建立關係
        parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
    //若該結點的元素爲“consumer”,則…
    } else if (ConsumerConfig.class.equals(beanClass)) {
        // 針對該結點“reference”屬性,迭代生成新的subBeanDefinition
        // subBeanDefinition的consumer值爲RuntimeBeanReference(id),建立關係
        parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
    }

    Set<String> props = new HashSet();
    ManagedMap parameters = null;
    // 通過反射獲取bean的所有方法, 然後循環遍歷方法獲取setXXX()格式的方法,獲取property
    // 然後根據property執行不同的邏輯,如property爲parameters時執行parseParameters()
    // 針對parameters,methods,arguments等分別執行不同的邏輯
    // 在這裏要吐槽下編碼者,不寫註釋要調試很多次才能看懂

    Method[] var28 = beanClass.getMethods();
    int len = var28.length;
    // …

    // 針對element結點在實體bean中沒有setXXX()方法的屬性,統一放在parameters中
    NamedNodeMap attributes = element.getAttributes();
    len = attributes.getLength();

    for(i = 0; i < len; ++i) {
        Node node = attributes.item(i);
        name = node.getLocalName();
        if (!props.contains(name)) {
            if (parameters == null) {
                parameters = new ManagedMap();
            }

            String value = node.getNodeValue();
            parameters.put(name, new TypedStringValue(value, String.class));
        }
    }

    if (parameters != null) {
        beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
    }

    return beanDefinition;
}

方法解析xml結點並以此生成bean對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章