在前幾天的“折騰了一把JAX-WS, SOA & Java EE 5”中(
http://www.javaresearch.org/article/116761.htm),
提到利用JAXB綁定的技術(shù),將包含業(yè)務(wù)邏輯的Schema綁定,自動(dòng)生成
Java源代碼。文中提到,生成的Java類(lèi)最好能在
網(wǎng)絡(luò )通訊中生存,即implements Serializable。另外,最好能在生成過(guò)程中,使生成類(lèi)型繼承共同的Super類(lèi),以便
于OOP技術(shù)的應用,如多態(tài)等。這樣,我們不用手工修改綁定生成的代碼。
上述目標可以通過(guò)對JAXB綁定的用戶(hù)化(Jaxb Binding Customization)來(lái)實(shí)現。
一般的做法是定義一個(gè)JAXB綁定的用戶(hù)化文件,并在我們的Schema文件中去引用就可以了。例如:
GlobalBindings.xsd
{code}
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
elementFormDefault="qualified"
jaxb:extensionBindingPrefixes="xjc" jaxb:version="2.0">
<xs:annotation>
<xs:appinfo>
<jaxb:globalBindings generateValueClass="true">
<xjc:simple/>
<xjc:serializable uid="1"/>
</jaxb:globalBindings>
</xs:appinfo>
</xs:annotation>
</xs:schema>
{code}
應注意的一點(diǎn)是,Schema的引用有兩個(gè)標簽,即<xs:include>和<xs:import>,前者要求被引用的xsd與引用者要有
相同的namespace;否則,就要使用import。如果所有的生成類(lèi)型都將繼承 Service,那只要在Service.xsd中將上
述GlobalBindings.xsd引用就可以了。在Schema中可用標簽<xs:extension>來(lái)定義這種繼承關(guān)系。
折騰來(lái)折騰去,發(fā)現一個(gè)JAXB不盡人意的地方,就是xjc在將xsd綁定位Java源代碼時(shí),當根據xsd的目錄結構,將生
成的源代碼也放置在相應的的目錄底下時(shí),xjc將在每個(gè)目錄下重復產(chǎn)生被引用的Schema的類(lèi)型。為了避免這種情況,
你只好將所有的Schema放置到同一目錄下,而產(chǎn)生的源代碼業(yè)將在同一目錄下。這可太討厭了,對于一定規模的SOA
應用來(lái)說(shuō),所有生成的請求及相應類(lèi)都放在同一目錄下是很難被接受的。
本文覺(jué)得有兩個(gè)辦法來(lái)實(shí)現生成的源代碼分目錄存放。一個(gè)是在綁定后,對源代碼進(jìn)行處理,反正是文本文件;另一個(gè)
辦法就是在編制Schema時(shí)就對此進(jìn)行考慮。本文將著(zhù)重討論后者,希望對讀者有些啟發(fā)。
Jaxb在對Schema進(jìn)行綁定時(shí),xjc提供了包參數選項,如果該參數沒(méi)有輸入,xjc將利用Schema中定義的namespace自
動(dòng)產(chǎn)生包名(package name)。如果你的“targetNamespace="http://service.t50/portable/svice”,
生成的包名將是:“t50.service.portable.svice”。
這樣,我們就可以利用xjc的這一缺省特點(diǎn),根據Schema的目錄結構,通過(guò)定義所需的namespace,來(lái)定義綁定后的源
代碼的目錄結構(也就是包名package name)。
假設我們的Schema目錄結構是這樣的:
“xsd/GlobalBindings.xsd”
“xsd/t50/Service.xsd”-- with namespace: "http://service.t50/portable"
“xsd/t50/ServiceCaller.xsd”-- with namespace: "http://service.t50/portable"
“xsd/t50/common/Person.xsd”-- with namespace: "http://service.t50/portable/common"
“xsd/t50/service/GetPerson.xsd”-- with namespace: "http://service.t50/portable/service"
通過(guò)xjc編譯后,生成的Java包類(lèi)則有:
“t50.service.portable”:ServiceRequest;ServiceResponse;ServiceCaller
“t50.service.portable.common”:Person
“t50.service.portable.service”:GetPersonRequest, GetPersonResponse
這樣,就基本上實(shí)現了綁定生成的類(lèi)型的分目錄存放目的了。本文基本上給出了一個(gè)通過(guò)JAXB綁定來(lái)實(shí)現一個(gè)SOA項目的
框架結構。Schema的定義是其中的關(guān)鍵部分,而本文的Schema定義原型已基本上初具規模,可以作為一個(gè)項目的開(kāi)始。
總體感覺(jué)是,Jaxb技術(shù)已經(jīng)成熟,但尚有很多可完善之處,尤其是xjc編譯輸出的靈活性等,有待優(yōu)化提高。
如果你有好的建議,或本文有錯謬之處,希望指出,大家一起提高。
以下是相關(guān)的Schema及相應的源代碼:
Service.xsd
{code}
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:sc="http://service.t50/portable"
targetNamespace="http://service.t50/portable"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:include schemaLocation="../GlobalBindings.xsd"/>
<xs:include schemaLocation="ServiceCaller.xsd"/>
<xs:complexType name="ServiceRequest">
<xs:sequence>
<xs:element name="ServiceCaller" type="sc:ServiceCaller" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ServiceResponse">
<xs:sequence>
<xs:element name="serviceMessage" minOccurs="0" maxOccurs="1">
<xs:complexType>
<!-- define response specific messages, like error messages etc -->
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
{code}
GetPerson.xsd
{code}
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:bc="http://service.t50/portable"
xmlns:cm="http://service.t50/portable/common"
xmlns:sc="http://service.t50/portable/service"
targetNamespace="http://service.t50/portable/service"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:import namespace="http://service.t50/portable"
schemaLocation="../Service.xsd"/>
<xs:import namespace="http://service.t50/portable/common"
schemaLocation="../common/Person.xsd"/>
<xs:element name="getPersonRequest" type="sc:GetPersonRequest" />
<xs:element name="getPersonResponse" type="sc:GetPersonResponse" />
<xs:complexType name="GetPersonRequest">
<xs:complexContent>
<xs:extension base="bc:ServiceRequest">
<xs:annotation>
<xs:documentation>Service request type</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="personId" type="xs:int" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="GetPersonResponse">
<xs:complexContent>
<xs:extension base="bc:ServiceResponse">
<xs:annotation>
<xs:documentation>Service response type</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="person" type="cm:Person" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
{code}
t50.service.portable.serivce.GetPersonRequest.java (comments removed)
{code}
package t50.service.portable.service;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import t50.service.portable.ServiceRequest;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"personId"
})
@XmlRootElement(name = "getPersonRequest")
public class GetPersonRequest
extends ServiceRequest
implements Serializable
{
private final static long serialVersionUID = 1L;
@XmlElement(namespace = "http://service.t50/portable/service")
protected int personId;
public int getPersonId() {
return personId;
}
public void setPersonId(int value) {
this.personId = value;
}
}
{code}
SOAP request payload: GetPersonRequest.xml
{code}
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<sc:getPersonRequest xmlns:sc="http://service.t50/portable/service"
xmlns:sc1="http://service.t50/portable"
xmlns:sc2="http://service.t50/portable/common"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://service.t50/portable/service GetPerson.xsd ">
<sc1:ServiceCaller>
<sc1:username>john</sc1:username>
<sc1:passwd>password</sc1:passwd>
</sc1:ServiceCaller>
<sc:personId>2008</sc:personId>
</sc:getPersonRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
{code}