从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和前面的定义一模一样