從Hearbeat說起
公司的運營部門要求所有上線的component有個鏈接,進行heartbeat檢測。
要求如下:
- 提供一個http url
- 這個http url 返回xml來顯示server的狀態,格式如下
heartbeat需要check哪些內容(db/io/thread/cpu/memory/file handler...),這裏不作討論。我看過的code中,有以下實現:
- 打開一個socket端口(非web app),如果系統自檢 ok, 返回一個OK的xml字符串。如果自檢失敗,返回fail的xml字符串。
- 定義一個servlet(web app),其他同上。
- 爲heartbeat定義一個wsdl(web app, deploy in axis2 aar ), 提供一個heartbeat service,比如:
http://host/app/UserService (domain service)
http://host/app/HeartbeatService (heartbeat service)
- 使用axis2的RawXMLINOutMessageReceiver或者AbstractInOutMessageReceiver(web app, deploy in axis2 aar),定義一個POJOs的service, 返回OMElement(可以從xml轉換得到)。本質和3相同,不過是少了wsdl
按照生成xml的方法,上面四種方式歸結爲3種
- raw string
- 定義xsd
- 使用DOM Tree (OMElement)。使用DOM Tree給人的感覺和raw string沒啥區別。
這裏就出現了JAXB的第一個應用場景: 需要生成xml,xml相對簡單:定義xsd有大材小用之嫌,代碼中字符串拼接又太ugly。
什麼是JAXB
Java Architecture for XML Binding(JAXB) 是一種OX(object <-> xml mapping)工具。主要提供了兩個功能:將 java對象編碼序列化(marshal)成xml,或者反之,從xml反序列化到java對象。一圖以蔽之:
- xml schema 與 classes(interfaces) 之間的相互轉換(爲下面的轉換做鋪墊)
- xml document 和 instance object 之間的相互轉換
xml是比java,c++, python...等編程語言更通用的描述語言,從語言相互翻譯的角度而言,上面兩種映射天然如此。
序列化任意java對象
回到本文開頭heartbeat xml生成的問題,
- 首先定義HeartBeat類,包含status。selfCheck方法檢測server的各種狀態,這裏僅僅是sample stub code,直接返回一個HeartBeat
- 使用JAXB將HeartBeat實例對象marshal成xml
- 生成的xml如下
說明:
Status被定義成static inner class而不是inner class,否則會報錯is a non-static inner class, and JAXB can’t handle those.
JAXBElement中Qname的local part爲admin,因爲xml的root elment從admin開始
在上面的例子中,Heartbeat類定義上未加任何的annotation,就直接被JAXB marshal。理論上,我們可以仿照上一篇A generic JAXB marshal/unmarshal XmlType中的泛型方法定義,將marshal方法定義成一個可marhal任意java對象的通用方法。不過在實際中,我們很少直接handle未加任何jaxb annotation的對象,主要有循環引用,內部類等問題。
上面的例子可以看出jaxb相當簡單易用(點此從一個實例看jaxb的強大),秉承JDK API的一貫風格。
XJC:schema -> java
如果遵循contract first的原則,在解決與xml相關的問題時,schema first.
- 首先定義heartbeat的schema如下(由xml->xsd在線工具得到):
- 使用xjc(jdk工具,與java/javac在同一目錄,在CMD命令窗口可直接使用),生成java類:xjc heartbeat.xsd
Admin
ObjectFactory
- 使用jaxb,將admin marshal。比前面少了兩行new JAXBElement對象的代碼
class定義中使用的Annotation和xml schema定義一一對應,說明如下:
@XmlRootElement(name = "admin") //xml 文檔根節點 |
<xsd:element name="admin"> |
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "status" }) @XmlElement(required = true) protected List<Admin.Status> status; //nested 的complexType,所以XmlType的name爲空;有Order indicator sequence,所以有propOrder來指定序列化順序 //maxOccurs爲unbounded,所以status定義爲List,默認minOccurs爲1,所以添加了required = true
//XmlAccessorType 表明了哪些field需要被序列化。
|
<xsd:complexType> <xsd:element maxOccurs="unbounded" name="status"> |
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "name", "value" })
//同上 |
<xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:int" /> <xsd:element name="value" type="xsd:int" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType>
</xsd:element> |
schemagen:java -> schema
如果你習慣先寫java class,或者已經有很多寫好的java code(有沒有jaxb annotation都成)。schemagen(同xjc,jdk工具)是幫助你快速定義schema的不二選擇。
對上面生成的Admin.java使用schemagen(schemagen Admin.java),生成的schema和前面的定義一模一樣