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了。