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

Dubbo框架內自定義很多XML標籤,方便以XML方式註冊服務;本篇文章先了解下Dubbo框架擁有哪些XML標籤,然後給出自定義標籤的流程並動手自定義標籤。

1.Dubbo框架中的那些標籤

Dubbo框架標籤定義的源文件是dubbo.xsd,該文件位於dubbo.jar內/META-INF/目錄下;源文件內容太多就不一一列出來,不過從dubbo.xsd得知Dubbo擁有以下15個標籤:annotation,application,module,registry,metadata-report,config-center,monitor,protocol,service,provider,consumer,reference,method,argument,parameters,每個標籤都定義好各自的元素,元素幾乎與各標籤對應的實體類的屬性相對應。

下面是使用dubbo自定義標籤的一個例子。
自定義標籤
爲了後面更好的學習Dubbo框架定義標籤的使用和解析,我們有必要了解標籤自定義的過程。

2.自定義標籤的流程

  • 定義標籤實體類和.xsd文件
  • 聲明標籤的命名空間及其處理類
  • 聲明標籤的解析邏輯
  • 編寫測試類

3.動手自定義標籤

3.1 定義標籤實體類和.xsd文件

標籤實體類Person:

public class Person {
    private String name;
    private String age;
    private int sex;
    private boolean flag;
    
    // 省略set get方法,toString方法
}

定義person.xsd文件

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
        xmlns="http://www.starry.net/schema/person"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="http://www.starry.net/schema/person"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans" />
    <!-- 定義element名, personType對應了bean的屬性  -->
    <xsd:element name="person" type="personType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The person config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>
    <!--  配置各屬性值,有點像Mybatis配置對應的model   -->
    <xsd:complexType name="personType">
        <xsd:attribute name="id" type="xsd:ID">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="name" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The person name. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="age" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The person age. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="sex" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The person sex. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="flag" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The person flag. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>

</xsd:schema>

person.xsd文件中的標籤元數據對應Person.class類中的屬性;.xsd文件中element的name值就是xml文件中使用的標籤名,attribute的name值則是標籤對應的屬性元素。注意attribute的type值,不需要與實體類中屬性的類型一致,統一寫成string類型。

person.xsd文件放置在/resources目錄下。

3.2 聲明標籤的命名空間及其處理類

person.xsd文件既然定義好了,那麼如何使用呢?在xml文件頭部通過自定義命名空間(xml name space)指定.xsd文件的schemasLocation,由該schemasLocation指定.xsd文件的位置。spring約定命名空間地址寫在/META-INF/spring.handlers文件內,並且指定該命名空間的處理類,即此處的PersonNamespaceHandler.class。

http\://www.starry.net/schema/person=com.starry.bean.handler.PersonNameSpaceHandler

等號左邊是自定義的命名空間地址,可以隨便定義;等號右邊是自定義命名空間處理類的類路徑名
編寫自定義命名空間處理類PersonNamespaceHandlers.class

package com.starry.bean.handler;

import com.starry.bean.parse.PersonBeanDefinitionParser;
import com.starry.custom.label.Person;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class PersonNameSpaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("person", new PersonBeanDefinitionParser(Person.class, true));
    }
}

命名空間指定.xsd文件的schemasLocation,一般都是配套寫在xml文件的頭部(命名空間地址和schemasLocation之間用空格或者換行隔開);spring約定schemaLocation寫在/META-INF/ spring.schemas文件內,由schemationLocation地址指定person.xsd文件的位置。

http\://www.starry.net/schema/person/person.xsd=person.xsd

等號左邊是約定的schemasLocation地址,它需要與上面定義的命名空間地址一致;等號右邊則是person.xsd在項目中的位置,如果放置在/META-INF目錄下,則需要指定目錄爲“/META-INF/person.xsd”。

3.3 聲明標籤的解析邏輯

在命名空間處理類PersonNamespaceHandler中有一個名爲PersonBeanDefinitionParser類,該類是解析xml文件person標籤的邏輯。

package com.starry.bean.parse;

import com.starry.custom.label.Person;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;


public class PersonBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    private final Class<?> beanClass;
    private final boolean required;

    public PersonBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }

    protected Class getBeanClass(Element element) {
        return Person.class;
    }

    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String name = element.getAttribute("name");
        String age = element.getAttribute("age");
        String sex = element.getAttribute("sex");
        String flag = element.getAttribute("flag");
        if (StringUtils.hasText(name)) {
            builder.addPropertyValue("name", name);
        }

        if (StringUtils.hasText(age)) {
            builder.addPropertyValue("age", age);
        }

        if (StringUtils.hasText(sex)) {
            builder.addPropertyValue("sex", Integer.valueOf(sex));
        }

        if (StringUtils.hasText(flag)) {
            builder.addPropertyValue("flag", Boolean.valueOf(flag));
        }
    }
}

繼承AbstractSingleBeanDefinitionParser類,重寫doParse(Element,BeanDefinitionBuilder)方法,定義簡單的解析邏輯。

3.4 編寫測試類

新建person.xml文件,引入perosn命名空間以及schemasLocation;然後聲明一個person標籤的對象。

<?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:test="http://www.starry.net/schema/person"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
         http://www.starry.net/schema/person
         http://www.starry.net/schema/person/person.xsd">

    <test:person id="a" name="xiaoMing" age="25" sex="1" flag="true" />

</beans>

xmlns:test="http://www.starry.net/schema/person"表示在person.xml文件內用test指定爲person命名空間地址的簡稱,所以就有了test:perosn。person是person.xsd文件內定義的element名稱,也是PersonNamespaceHandler.class處理person命名空間的依據。

注意:id屬性是每個標籤都有的,可以聲明也可以省略。

編寫test類,獲取容器中person對象並打印出來。

package com.starry;

import com.starry.custom.label.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("person.xml");
        Person person =  context.getBean(Person.class);
        System.out.println(person.toString());
    }
}

執行結果表明person.xml文件內test:person標籤的內容已經被初始化爲容器內的bean,所以自定義標籤person成功。

4.思考

早在第二小節就給出了自定義標籤的具體步驟,也是爲了幫助不熟悉自定義標籤的讀者更好的理解這個過程;在第三節的演示過程中,詳細說明每個步驟的作用和含義,雖然最終實踐成功,但是你可能好奇PersonNamespaceHandler和PersonDefinitionParser類在自定義標籤解析的過程中是如何執行的,或者說什麼時候被執行的。

要想理解上面的問題,就必須深入ClassPathXmlApplicationContext類的初始化過程,這個過程我們在剖析Dubbo自定義標籤解析流程中會深入分析,這裏我們對自定義標籤的流程有一個認識就OK了。

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