使用 JAXB 框架定制 Web 服务行为

红薯 发布于 2010/03/24 09:45
阅读 1K+
收藏 3

Web 服务概述

从本质上来讲 Web 服务就是对外暴露的并能够通过 Web 进行调用的一组 API,它并不要求服务提供方及调用方的技术实现、编程语言、运行平台甚至操作系统等。也正因为这个特点 Web 服务能在企业应用集成领域迅速占领一席之地,并成为实施应用程序集成的首选手段之一,同时 Web 服务也是面向服务架构的重要实现手段。

深入剖析 Web 服务我们不难发现在这个简单定义的背后有若干问题需要解决:Web 服务提供程序如何描述自己才能为调用程序所理解;调用程序如何发现可用的 Web 服务;调用程序在找到需要的 Web 服务后应该如何调用特定的接口。目前业界已经有多个标准以及协议来解决上述问题并支撑 Web 服务的使用,其中最重要的非 WSDL、SOAP、UDDI 莫属。本文以 Java Web 服务为例介绍如何定制行为。

Web 服务基本构件

WSDL、SOAP 和 UDDI 是 Web 服务中最基础也是最重要的组成部分,这三个协议定义了 Web 服务的描述,发现以及访问。

WSDL(Web Service Description Language):Web 服务描述语言。每个 Web 服务都会有一个相应的 WSDL 文档来进行描述。WSDL 文档首先对接口和访问时的请求 / 响应消息进行抽象描述,然后将其绑定到具体的传输协议和消息格式上以最终定义具体部署的服 务访问点。WSDL 文档本质上是一个 XML 格式的文档,其中详细定义了 Web 服务所暴露的接口集合,接口原型,接口参数、返回类型的数据结构,接口调用消息格式,承载协议等。

SOAP(Simple Object Access Protocol):简单对象访问协议。SOAP 协议本质上就是 XML 和 HTTP 协议的组合体,它定义了一个 XML 格式的 Web 服务访问消息封装格式并通过 HTTP 协议进行消息承载,当然 SOAP 消息也可以通过 HTTPS 或者 SMTP 等协议来承载。

UDDI(Universal Description, Discovery and Integration):统一描述、发现和集成协议。它是为解决 Web 服务的发布和发现问题而制订的技术标准,包含一组基于 Web 的、分布式的 Web 服务信息注册中心的实现标准,以及一组使企业能将自己提供的 Web 服务注册到该中心的实现标准。

Web 服务中的数据绑定

通俗来讲,XML 形式数据和应用程序业务数据结构的转换过程就称为数据绑定。XML 以其特有的平台独立性可以很好的用来描述 Web 服务,但是 Web 服务的核心仍在于应用程序业务逻辑处理,在业务逻辑处理中需要的业务数据结构而非这些 XMl 格式的数据,因此就需要数据绑定来完成从 XML 格式数据到应用程序业务数据结构的转换。从上节我们不难看出 Web 服务在通信过程中均以 XML 形式进行表示的,而在 Java Web 服务的提供端和调用端又是以 Java 对象形式存在,因此就必然需要一个 XML 和 Java 之间的一个双向转换过程,这个转换过程就是 XML 和 Java 的数据绑定。目前业界有很多数据绑定实现,比如 adb、xmlbeans、jibx、jaxme、jaxb 等,可以根据项目实际情况进行选择,像 Axis2 里就是用了自己的 adb 绑定框架,而 JAX-RPC 类型 Web 服务就有自己的绑定机制。下图直观地反映了 Java Web 服务调用过程中的数据绑定:


图 1 .Web 服务中的数据绑定 Web 服务类型 :JAX-WS vs JAX-RPC
图 1 .Web 服务中的数据绑定 Web 服务类型 :JAX-WS vs JAX-RPC

JAX-RPC 规范定义了如何使用 Java 语言来实现 Web 服务,这个实现是基于 Java 的 RPC, 并不完全支持 XML schema 规范,同时没有对数据绑定定义标准的实现。 JAX-WS 是 JAX-RPC 的后续版本,它在协议支持、接口映射模型以及消息传输等方面均作了较大提升和优化。同时 JAX-WS 映射到 Java 5.0 版本,它大量使用了 Java 5.0 版本新特性诸如注解、泛型支持等,而 JAX-RPC 映射到 Java 1.4 版本。此外一个重要区别就是 JAX-WS 将数据绑定交给 JAXB 来处理,而 JAX-RPC 像之前提到的定义了一套非标准的私有绑定机制,这也直接导致了在 JAX-RPC Web 服务中开发者很难对 Web 服务进行数据绑定的定制。本文后续所指 Web 服务默认为 JAX-WS 型 Web 服务。

JAXB 框架概述

JAXB(Java Architecture for XML Binding)是由 J2EE 体系中标准的 XML schema 和 Java 数据绑定框架,开发者可以使用 JAXB 方便地在 Java 应用程序中进行 XML 数据和 Java 对象的转换。JAXB 提供了将 XML 文档解析为 Java 内容树的方法,以及将 Java 内容树重新写回 XML 文档的方法。同时 JAXB 还提供了基于 XML schema 生成 Java 类以及从 Java 类生成 XML schema 的支持。JAXB 框架分为以下三个组成部分:

schema 编译器:schema 编译器以 XML schema 为输入生成和 schema 相对应的 Java 类集,生成的 Java 类中会根据 schema 的结构自动包含了相应的 JAXB 注解。比方说 schema 里的一个元素映射的 Java 类会自动用 @XmlType 注解加以标注,一个元素的属性所映射的 Java 字段会自动用 @XmlElement 注解加以标注;

schema 生成器:schema 生成器以包含 JAXB 注解的 Java 类集为输入生成相应的 XML schema。比方说一个包含 @XmlType 注解的 Java 类在生成的 schema 中会被映射为一个元素;

JAXB 运行环境:JAXB 运行时环境提供了两个基本的操作用于 XML 文档的访问,操作以及验证等等:marshal、unmarshal,这两个操作也是 XML 和 Java 绑定的基础。Marshal 是指将一个内存对象以 XML 文档形式进行编组处理的过程,unmarshal 则是一个相反的过程,将一个 XML 文档解析为内存对象的过程。


图 2. JAXB 框架结构示意图
图 2 JAXB 框架结构示意图

JAXB 的基础 -XML schema

XML schema 是 XML 文档的格式定义,是 DTD 的替代产品用于约束 XML 文档内容及结构,比如它定义了 XML 文档可以出现的元素,元素出现的顺序,元素具有的属性等。XML schema 本身就是一个 XML 文档,因此天然具有了 XML 的可扩展性。同时 XML schema 对 XML 数据类型提供了更为丰富的支持。JAXB 框架的基础就是 XML schema,一个使用 JAXB 的典型场合就是根据业务数据模型需要先写出 XML schema,然后利用 JAXB 框架的绑定编译器直接生成 XML 相应的 Java 类集。

JAXB 与 Web 服务关系

JAX-WS 型 Web 服务将 XML 和 Java 之间的绑定代理给了 JAXB 来完成,也就是说在 Web 服务调用过程中的 XML 数据和 Java 对象间转换都是由 JAXB 框架来完成的。Web 服务中接口引用的参数、返回类型等等数据类型描述都是在关联的 XML schema 中定义的,而 JAXB 框架也正是根据这个 Web 服务关联的 XML schema 来完成于 Java 对象之间的相互映射。

JAXB 框架定制支持

JAXB 规范定义了 XML schema 与 Java 的标准映射关系,比方说 XML 的 xsd:string 数据类型映射为 Java.lang.String,所有 JAXB 规范实现者必须实现规范所定义的标准的 XML schema 与 Java 的映射。在大多数情况下标准映射已经能满足开发者编程需要,但是不排除在一些特殊场合我们需要定制或重载 JAXB 规范定义的标准映射,比方说我们想把 xsd:string 映射到我们自己的一个 Javabean 而非标准映射里的 Java.lang.String 类,或者我们希望对 XML schema 编译器产生的 Java 类集增加 Java doc 支持,甚至定制产生的 Java 类的名字等等,这个时候我们就需要对 JAXB 标准映射进行定制。

JAXB 提供了两种方式支持从 XML schema 到 Java 的绑定定制,定制通常是以定制声明形式完成。

schema 内置注解:开发者可以通过在 XML schema 文档中用注解方式进行定制声明,这种定制方式开发者可以直观的看到定制声明的作用对象,作用域等,通常用于比较简单的绑定定制。schema 内置定制声明通常的语法格式如下:

<xs:annotation>
<xs:appinfo>
.....
binding declaration
.....
</xs:appinfo>
</xs:annotation>

 

schema 外部绑定定制文件:开发者可以把定制声明编写在一个单独的文件中,然后在编译 schema 时指定这个定制文件,这类定制文件通常以 .xjb 扩展名命名。这种方式便于 schema 定制声明的集中化管理。外部绑定定制文件中的定制声明通常的语法格式如下:

<jxb:bindings>
<jxb:bindings>
<binding declaration>
<jxb:bindings>
</jxb:bindings>

 

从 Java 到 XML schema 的映射开发者可以通过 JAXB 提供的注解来完成,在编程 Java 类时可以使用 JAXB 丰富的注解轻松地完成绑定定制。

开发者可以在全局、schema、组件等不同层次上进行进行定制声明,相应不同的声明作用域。

使用 JAXB 框架定制 Web 服务

自底向上编程模型

自底向上编程模型是指开发者从编写 Java 类开始,然后将之发布为 Web 服务。自底向上编程模型通常在 Web 服务提供方被采用,开发者在 Java 类的开发过程中可以使用 Java 注解来完成对 Web 服务的行为定制。

JAXB 框架提供了丰富的注解供开发者使用来完成从 Java 类开始定制 Web 服务,当然这个定制局限在数据绑定层面上。在这种编程模型中 JAXB 主要完成从 Java 类到 XML schema 的映射。JAX-WS 规范本身也定义了若干注解用于 Web 服务的基础行为定制 ( 比方说服务名称,命名空间等 )。本文我们重点关注如何利用 JAXB 来定制 Web 服务,因此下面我们主要结合 Webshpere 应用服务器 Web 服务实现来介绍一些在编程中常用的注解并介绍相应的 Web 服务行为。

@XmlRootElement

@XmlRootElement 注解用于标注类或枚举类型,用它标注的类在映射后的 schema 中会以一个全局元素的形式出现,元素的类型是一个包含 Java 类属性的 XML 复杂数据类型。我们可以通过 @XmlRootElement 注解的 name 属性来定制映射的 schema 全局元素的名称,一般来说以 @XmlRootElement 标注的类在相应的 XML 文档中会以最外层或根节点形式出现。比方说有如下标注类:

@XmlRootElement(name="RootStudent",namespace="http://service.dsw.ibm.com/")
public class Student {
...
}
这个 Java 类在映射后的 Web 服务 schema 中会表现为:
<xs:element name="RootStudent" type="tns:student"/>
<xs:complexType name="student">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

 

@XmlType

@XmlType 注解用于标注类或枚举类型,用它标注的类在映射后的 schema 中中会以一个 XML 复杂数据类型的形式出现。我们可以通过 @XmlType 注解的 name 属性来定制映射的 XML 数据类型的名称,用 propOrder 属性来定制映射后的复杂数据类型的内容顺序等。比方说有如下标注类:

@XmlType(name = "CustomizedStudent", propOrder = { "name", "id", "age" })
public class Student {
private int id;
private int age;
private String name;
....
}

 

这个 Java 类在映射后的 Web 服务 schema 中会表现为:

<xs:complexType name="CustomizedStudent">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="id" type="xs:int"/>
<xs:element name="age" type="xs:int"/>
</xs:sequence>
</xs:complexType>

 

@XmlElement

@XmlElement 注解用于标注 Javabean 的属性,用它标注的属性在映射后的 schema 中以元素的形式出现,所有 Javabean 属性映射的元素组合成为 @XmlType 映射的复杂数据类型。我们可以通过 @XmlElement 注解的 name 属性定制映射后的 XML 元素的名称,用 required 属性来指定该元素是否必须出现,用 nillable 属性来指明该元素是否允许空值。比方说有如下标注类:

@XmlType(name = "CustomizedStudent", propOrder = { "name", "id", "age"})
public class Student {
@XmlElement(name="StudentID",required=true)
private int id;
@XmlElement(name="StudentAge",nillable=true)
private int age;
@XmlElement(name="StudentName")
private String name;
}

 

这个 Java 类在映射后的 Web 服务 schema 中会表现为:

<xs:complexType name="CustomizedStudent">
<xs:sequence>
<xs:element minOccurs="0" name="StudentName" type="xs:string"/>
<xs:element name="StudentID" type="xs:int"/>
<xs:element name="StudentAge" nillable="true" type="xs:int"/>
</xs:sequence>
</xs:complexType>

 

@XmlAttribute

@XmlAttribute 注解用于标注 Javabean 属性,用它标注的属性在映射后的 schema 中以元素属性形式表现。我们可以通过 @XmlAttribute 注解的 name 属性来定制映射后的名称,用 required 属性来指定是否映射后的属性为必须出现的。比方说有如下标注类:

@XmlType(name = "CustomizedStudent", propOrder={"name","id","age"})
public class Student {
@XmlElement(name="StudentID")
private int id;
@XmlAttribute(name="StudentAge")
private int age;
@XmlAttribute(name="StudentName",required=true)
private String name;
}

 

这个 Java 类在映射后的 Web 服务 schema 中会表现为:

<xs:complexType name="CustomizedStudent">
<xs:sequence>
<xs:element name="StudentID" type="xs:int"/>
</xs:sequence>
<xs:attribute name="StudentName" type="xs:string" use="required"/>
<xs:attribute name="StudentAge" type="xs:int" use="required"/>
</xs:complexType>

 

以上着重介绍了在 Web 服务开发中比较常用的几个 JAXB 注解来定制 Java 到 XML 的数据绑定从而达到定制 Web 服务的目的。JAXB 框架中还有丰富的注解供开发者根据实际开发需要选择使用。

自顶向下编程模型

自顶向下编程模型是指开发者从 Web 服务的 WSDL 文档开始编程,典型的过程是开发者首先根据 WSDL 文档生成服务代理类和 schema 对应的 Java 类集,这种编程模型通常用于 Web 服务客户端程序。在这种编程模型下 JAXB 主要完成从 XML schema 到 Java 类的映射,可以使用 JAXB 提供的定制声明来实现映射关系的定制。

定制声明语法

JAXB 框架提供了四种类型绑定声明:全局绑定声明、schema 绑定声明、类绑定声明、属性绑定声明,它们分别对应着绑定声明不同的作用域。

全局绑定声明:在整个 schema 范围内有效 ( 对于包含的子 schema 仍然有效 )。全局声明必须出现在 schema 文档中最顶级元素,而且在给定的 schema 或绑定定制文件中只能声明一次。比如我们可以通过 globalBindings 的 collectionType 属性来指定在映射的 Java 类中容器类使用我们指定的容器类。语法格式如下:

<globalBindings>
[ collectionType = "collectionType" ]
[ fixedAttributeAsConstantProperty = "true" | "false" | "1" | "0" ]
[ generateIsSetMethod = "true" | "false" | "1" | "0" ]
[ enableFailFastCheck = "true" | "false" | "1" | "0" ]
[ choiceContentProperty = "true" | "false" | "1" | "0" ]
[ underscoreBinding = "asWordSeparator" | "asCharInWord" ]
[ typesafeEnumBase = "typesafeEnumBase" ]
[ typesafeEnumMemberName = "generateName" | "generateError" ]
[ enableJavaNamingConventions = "true" | "false" | "1" | "0" ]
[ bindingStyle = "elementBinding" | "modelGroupBinding" ]
[ <JavaType> ... </JavaType> ]*
</globalBindings>

 

schema 绑定声明:通过这个注解可以实现从 XML schema 到产生 Java 类 / 接口的名称和包路径映射定制。比如我们可以通过指定 schemaBindings 的 package 属性来指定映射后 Java 类的包路径名称,通过 nameXmlTransform 属性来指定映射的 Java 类 / 接口名的前缀、后缀等。语法格式如下:

<schemaBindings>
[ <package> package </package> ]
[ <nameXmlTransform> ... </nameXmlTransform> ]*
</schemaBindings>
<package [ name = "packageName" ]
[ <Javadoc> ... </Javadoc> ]
</package>
<nameXmlTransform>
[ <typeName [ suffix="suffix" ]
[ prefix="prefix" ] /> ]
[ <elementName [ suffix="suffix" ]
[ prefix="prefix" ] /> ]
[ <modelGroupName [ suffix="suffix" ]
[ prefix="prefix" ] /> ]
[ <anonymousTypeName [ suffix="suffix" ]
[ prefix="prefix" ] /> ]
</nameXmlTransform>

 

类绑定声明:XML schema 元素 / 复杂类型可能映射为一个 Java 类 / 接口,可以通过类绑定声明可以实现从 XML schema 元素 / 复杂类型到 Java 类 / 接口的映射定制。比如可以通过指定 class 的 name 属性来指定映射的 Java 类 / 接口名称等。类绑定声明只对声明的元素 / 复杂类型有效。语法格式为:

<class [ name = "className"]
[ implClass= "implClass" ] >
[ <Javadoc> ... </Javadoc> ]
</class>

 

属性绑定定制:XML schema 复杂数据类型引用的元素映射为 Java 类的属性,可以通过这个绑定声明可以实现从 XML schema 元素到 Java 类的属性的映射定制。比如可以通过指定 property 的 name 属性来指定映射 Java 类的属性名称,通过嵌套的 baseType 属性来指定映射 Java 类的属性类型等。语法格式为:

<property      [ name = "propertyName"]
[ collectionType = "propertyCollectionType" ]
[ fixedAttributeAsConstantProperty = "true" | "false" | "1" | "0" ]
[ generateIsSetMethod = "true" | "false" | "1" | "0" ]
[ enableFailFastCheck ="true" | "false" | "1" | "0" ]
[ <baseType> ... </baseType> ]
[ <Javadoc> ... </Javadoc> ]
</property>
<baseType>
<JavaType> ... </JavaType>
</baseType>

 

此外 JAXB 还提供了 JavaType 绑定声明来定制 XML 数据类型和 Java 数据类型的映射。因为 XML 比 Java 提供了更丰富的数据类型,所以当 JAXB 提供的默认 XML 数据类型和 Java 数据类型映射不能满足开发实际需求时可以考虑使用这个注解来定制。映射的 Java 数据类型可以是 Java 内置类型或用户自定义类型,对于用户自定义类型必须提供编组和反编组方法。JavaType 声明可以嵌套在全局和属性绑定声明中。语法格式为:

<JavaType name= "JavaType"
[ xmlType= "xmlType" ]
[ hasNsContext = "true" | "false" ]
[ parseMethod= "parseMethod" ]
[ printMethod= "printMethod" ]>
</JavaType >

 

Javadoc 绑定声明可以用来定制基于 schema 产生的类 / 包的 API 文档,它可以嵌套在 schema 和类绑定声明中。语法格式为:

<Javadoc>
Contents in Javadoc format.
</Javadoc>

 

JAXB 框架提供的这些绑定声明中各个属性参数的具体含义可以参考相关文档,这里不再一一详解。在了解完绑定声明的语法格式后,下边介绍下如何在 JAXB 中使用这些定制声明。通过从前面章节我们已经知道 JAXB 提供了两种途径来定制从 XML schema 到 Java 的映射,下面就针对这两种途径加以详细介绍。

Schema 内置注解

Schema 内置注解就是将绑定声明定义在当前 schema 文档内,这种方式适用于比较简单的映射定制。在此种方式下需要绑定声明遵从的 JAXB 版本,它是 xs:schema 节点的一个属性,例如 jaxb:version="2.0"。下边我们举例来说明如何在 schema 文档内定义绑定声明。

比方说我们想在全局范围下希望将 XML 的 date 数据类型映射为 Java.util.Date 类型。如图 3.1 示例代码在全局绑定声明中嵌套了 JavaType 来将 xs:date 映射为 Java.util.Date 类型,unmarshal 方法为 Javax.xml.bind.DatatypeConverter.parseDate。


图 3. schema 内置注解定制数据类型映射 -XML 代码
图 3 . schema 内置注解定制数据类型映射 -XML 代码

经过 JAXB 的 schema 编译器映射后相应的 Java 代码如图 3.2,可以清楚地看到 birthDate 在 Student 类中的数据类型为 Java.util.Date。


图 4. schema 内置注解定制数据类型映射 -Java 代码
图 4. schema 内置注解定制数据类型映射 -Java 代码

下面我们再看下如果通过 Javadoc 来定制产生的 Java 类的 API 文档,在如下 schema 中我们将 birthDate 元素映射为 Java.util.Calendar 类型,同时定制映射后的属性名称为 studentBirthDate,定制这个属性的 Javadoc 为”The Birth Date is the birth date of student.”


图 5. schema 内置注解定制 Javadoc-xml 代码
图 5. schema 内置注解定制 Javadoc-xml 代码

经过 JAXB 的 schema 编译器映射后相应的 Java 代码如图 3.4,可以清楚地看到 birthDate 在 Student 类中的数据类型为 Java.util.Calendar,Javadoc 正是期望的。


图 6. schema 内置注解定制 Javadoc -Java 代码
图 6 schema 内置注解定制 Javadoc -Java 代码

Schema 外部绑定定制文件

Schema 外部绑定定制文件就是将绑定声明定义在一个外部独立的文件 ( 通常以 .xjb 为扩展名 ) 中而非定义在当前 schema 文档内。

一般以 <jaxb:bindings> 为根节点的 XML 文档就被认为是 schema 外部绑定文件,文件的根节点必须指定绑定声明遵从的 JAXB 版本号,同时文件还需要定义命名空间声明,schema 名称和 node 声明用来指定绑定声明作用的 schema 和节点等。比如我们可以将前述的定制声明移到以下外部绑定文件中:


图 7. Schema 外部绑定定制文件 -XML 代码
图 7. Schema 外部绑定定制文件 -XML 代码

在运行 schema 编译器时以 -b 参数指定这个定制文件,生成的 Java 代码如下:


图 8. Schema 外部绑定定制文件 -Java 代码
图 8. Schema 外部绑定定制文件 -Java 代码

从上例我们可以看到不管用 schema 内置注解还是外部绑定定制文件可以达到相同的定制效果,开发者可以根据实际来选择使用哪种。

JAXB 数据绑定运行

本文中所有代码均在实际应用服务器环境下运行,结尾处可以找到示例 schema 和 Java 类的下载,读者可以在本地环境实验。相关的 schema 编译器和产生器命令文件如下,运行时请参考命令参数的帮助。

%app-server%/bin/xjc.bat
%app-server%/bin/schemagen.bat

结束语

本文重点介绍了如何使用 JAXB 框架定制 Web 服务行为,对从 Java 到 XML schema 和从 XML schema 到 Java 的映射定制均做了详细的介绍。

加载中
返回顶部
顶部