在前几天的“折腾了一把JAX-WS, SOA & Java EE 5”中(http://www.javaresearch.org/article/116761.htm),
提到利用JAXB绑定的技术,将包含业务逻辑的Schema绑定,自动生成Java源代码。文中提到,生成的Java类最好能在
网络通讯中生存,即implements Serializable。另外,最好能在生成过程中,使生成类型继承共同的Super类,以便
于OOP技术的应用,如多态等。这样,我们不用手工修改绑定生成的代码。
上述目标可以通过对JAXB绑定的用户化(Jaxb Binding Customization)来实现。
一般的做法是定义一个JAXB绑定的用户化文件,并在我们的Schema文件中去引用就可以了。例如:
GlobalBindings.xsd
{code}
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">
{code}
应注意的一点是,Schema的引用有两个标签,即和,前者要求被引用的xsd与引用者要有
相同的namespace;否则,就要使用import。如果所有的生成类型都将继承 Service,那只要在Service.xsd中将上
述GlobalBindings.xsd引用就可以了。在Schema中可用标签来定义这种继承关系。
折腾来折腾去,发现一个JAXB不尽人意的地方,就是xjc在将xsd绑定位Java源代码时,当根据xsd的目录结构,将生
成的源代码也放置在相应的的目录底下时,xjc将在每个目录下重复产生被引用的Schema的类型。为了避免这种情况,
你只好将所有的Schema放置到同一目录下,而产生的源代码业将在同一目录下。这可太讨厌了,对于一定规模的SOA
应用来说,所有生成的请求及相应类都放在同一目录下是很难被接受的。
本文觉得有两个办法来实现生成的源代码分目录存放。一个是在绑定后,对源代码进行处理,反正是文本文件;另一个
办法就是在编制Schema时就对此进行考虑。本文将着重讨论后者,希望对读者有些启发。
Jaxb在对Schema进行绑定时,xjc提供了包参数选项,如果该参数没有输入,xjc将利用Schema中定义的namespace自
动产生包名(package name)。如果你的“targetNamespace="http://service.t50/portable/svice”,
生成的包名将是:“t50.service.portable.svice”。
这样,我们就可以利用xjc的这一缺省特点,根据Schema的目录结构,通过定义所需的namespace,来定义绑定后的源
代码的目录结构(也就是包名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"
通过xjc编译后,生成的Java包类则有:
“t50.service.portable”:ServiceRequest;ServiceResponse;ServiceCaller
“t50.service.portable.common”:Person
“t50.service.portable.service”:GetPersonRequest, GetPersonResponse
这样,就基本上实现了绑定生成的类型的分目录存放目的了。本文基本上给出了一个通过JAXB绑定来实现一个SOA项目的
框架结构。Schema的定义是其中的关键部分,而本文的Schema定义原型已基本上初具规模,可以作为一个项目的开始。
总体感觉是,Jaxb技术已经成熟,但尚有很多可完善之处,尤其是xjc编译输出的灵活性等,有待优化提高。
如果你有好的建议,或本文有错谬之处,希望指出,大家一起提高。
以下是相关的Schema及相应的源代码:
Service.xsd
{code}
xmlns:sc="http://service.t50/portable"
targetNamespace="http://service.t50/portable"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" attributeFormDefault="unqualified">
{code}
GetPerson.xsd
{code}
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">
schemaLocation="../Service.xsd"/>
schemaLocation="../common/Person.xsd"/>
Service request type
Service response type
{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}
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
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 ">
john
password
2008
{code}