JAX-WS数据映射
参考:
http://www.ibm.com/developerworks/cn/webservices/ws-tip-jaxwsrpc2.html
http://www.ibm.com/developerworks/cn/webservices/ws-tip-jaxwsrpc.html
概述
JAX-WS 2.0 是 JAX-RPC 1.1 的后续版本,通过使用 JAXB (Java Architecture for XML Binding),一种 JCP 定义的技术,它对数据映射方法进行了改进。
尽管 JAX-WS 2.0 中的一些方面是在 JAX-RPC 1.1 的基础上进行的改进,但其他的部分却是革命性的。例如,JAX-WS 没有提供 XML 模式和 Java 之间的映射,而这是 JAX-RPC 1.1 中的一个重要特性。相反,JAX-WS 使用了另一种 JCP 定义的技术 JAXB (Java Architecture for XML Binding) 2.0,为其完成数据映射。JAX-WS 仅仅提供了 Web 服务调用模型。它不再关心表示应用程序数据的 Java Bean,而仅仅关注于将其提供给目标 Web 服务。
简单类型映射
表 1. JAX-RPC 1.1 和 JAXB 2.0 中 XML 简单类型映射的差异
类型 |
JAX-RPC 1.1 |
JAXB 2.0 |
xsd:anySimpleType |
java.lang.String |
java.lang.Object |
xsd:duration |
java.lang.String |
javax.xml.datatype.Duration(新的类型) |
xsd:dateTime |
java.util.Calendar |
javax.xml.datatype.XMLGregorianCalendar(新的类型) |
xsd:time |
java.util.Calendar |
javax.xml.datatype.XMLGregorianCalendar |
xsd:date |
java.util.Calendar |
javax.xml.datatype.XMLGregorianCalendar |
xsd:gYearMonth |
java.lang.String |
javax.xml.datatype.XMLGregorianCalendar |
xsd:gYear |
java.lang.String |
javax.xml.datatype.XMLGregorianCalendar |
xsd:gMonthDay |
java.lang.String |
javax.xml.datatype.XMLGregorianCalendar |
xsd:gMonth |
java.lang.String |
javax.xml.datatype.XMLGregorianCalendar |
xsd:gDay |
java.lang.String |
javax.xml.datatype.XMLGregorianCalendar |
xsd:anyURI |
java.net.URI |
java.lang.String |
xsd:NMTOKENS |
java.lang.String[] |
java.util.List<java.lang.String> |
xsd:IDREF |
java.lang.String |
java.lang.Object |
xsd:IDREFS |
java.lang.String[] |
java.util.List<java.lang.Object> |
xsd:ENTITY |
not supported |
java.lang.String |
xsd:ENTITIES |
not supported |
java.util.List<java.lang.String> |
在 JAX-RPC 和 JAXB 之间,简单类型映射的纯 Java 方面几乎相同,但是 JAXB 映射还使用了新的 Java 注释特性。
在 JAX-RPC 1.1 所生成的 Java Bean 中,您无法区分下面的差别:
- 元素字段和属性字段
- 从 minOccurs="0" type="xsd:int" 映射的字段和从 nillable="true" type="xsd:int" 映射的字段
- 从 type="xsd:string" 映射的字段和从 type="xsd:string" minOccurs="0" 映射的字段
但由于 JAXB 使用了新的 Java 注释,现在您可以区分出它们之间的差别。@XmlElement 和 @XmlAttribute 注释具有一些选项。其中与本文相关的选项包括:
- Required:该元素是否必须存在?例如,minOccurs 是否不等于 1?
- Nillable:该字段是否包含 nillable="true" 属性
XML complexType 元素和属性 <xsd:element name="intField" type="xsd:int"/> <xsd:element name="intMinField" type="xsd:int" minOccurs="0"/> <xsd:element name="stringMinField" type="xsd:string" minOccurs="0"/> <xsd:element name="stringNilField" type="xsd:string" nillable="true"/>
<xsd:attribute name="intAttr" type="xsd:int"/> <xsd:attribute name="intAttrReq" type="xsd:int" use="required"/> |
通过 JAXB 2.0 映射为 Java Bean 属性 protected int intField; protected Integer intMinField; @XmlElement(required = true) protected String stringMinField; @XmlElement(required = true, nillable = true) protected String stringNilField;
@XmlAttribute protected Integer intAtt; @XmlAttribute(required = true) protected int intAttReq; |
数组映射
在 JAX-RPC 和 JAXB 之间,从 XML 到 Java 的数组映射存在着差异,这是因为 JAXB 使用了新的泛型 Java 特性.
注意访问器方法中的差别。JAX-RPC 映射遵循 Java Bean 数组访问器的严格定义。JAXB 映射并没有映射为数组,所以有些不同。getIntArrayField 返回一个引用,不仅仅是快照,而是实际的列表。因此,您对返回的列表所做的任何修改都将出现在属性中。这就是为什么对于数组属性没有 set 方法的原因。
Address.java |
@XmlElement(required = true) protected List<String> street; public List<String> getStreet() { if (street == null) { street = new ArrayList<String>(); } return this.street; } |
复杂类型的映射
在 JAX-RPC 和 JAXB 中,对 complexType 的映射几乎相同,除了这些字段在 JAX-RPC 中是私有的,而在 JAXB 中是保护的,并且 JAXB 映射添加了相应的注释对字段顺序进行描述。
Address.java |
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Address", propOrder = { "name", "street", "city", "country" }) public class Address { @XmlElement(required = true) protected String name; @XmlElement(required = true) protected List<String> street; @XmlElement(required = true) protected String city; @XmlElement(required = true) protected String country; |
ObjectFactory 类
JAXB 生成了一个文件 ObjectFactory,而 JAX-RPC 则没有。每个包含 Java Bean 的目录都会有一个 ObjectFactory。对于在模式相应的命名空间中定义的每种类型,ObjectFactory 类都为该类型提供了一种创建方法。对于每个元素,ObjectFactory 类提供了一种创建元素的方法,该方法返回一个 javax.xml.bind.JAXBElement<Type>。例如,下面JAXBElement<BookOrderResponse>的例子,这个ObjectFactory类具有一个返回BookOrder的实例和一个返回 JAXBElement<BookOrder> 的实例的方法。您仍然可以直接实例化该目录中的 Java Bean,但最好是使用这个工厂。
ObjectFactory.java |
package org.example.cxfstp04;
import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlRegistry; import javax.xml.namespace.QName;
@XmlRegistry public class ObjectFactory {
private final static QName _BookOrderResponse_QNAME = new QName("http://www.example.org/cxfstp04/", "BookOrderResponse"); private final static QName _BookOrder_QNAME = new QName("http://www.example.org/cxfstp04/", "BookOrder");
public ObjectFactory() { } public USAddress createUSAddress() { return new USAddress(); } // 返回BookOrder的方法 public BookOrder createBookOrder() { return new BookOrder(); } public BookOrderResponse createBookOrderResponse() { return new BookOrderResponse(); } public Book createBook() { return new Book(); } public UKAddress createUKAddress() { return new UKAddress(); } public BriefUSAddress createBriefUSAddress() { return new BriefUSAddress(); } public Address createAddress() { return new Address(); } @XmlElementDecl(namespace = "http://www.example.org/cxfstp04/", name = "BookOrderResponse") public JAXBElement<BookOrderResponse> createBookOrderResponse(BookOrderResponse value) { return new JAXBElement<BookOrderResponse>(_BookOrderResponse_QNAME, BookOrderResponse.class, null, value); } @XmlElementDecl(namespace = "http://www.example.org/cxfstp04/", name = "BookOrder") //返回一个JAXBElement<BookOrder>的方法 public JAXBElement<BookOrder> createBookOrder(BookOrder value) { return new JAXBElement<BookOrder>(_BookOrder_QNAME, BookOrder.class, null, value); } } |
实现代码
实现的业务代码包括:
- 数据类实现.
- OrderBook服务的实现.
- 测试的客户代码.
数据类实现
Address.java |
protected List<String> street; public List<String> getStreet() { if (street == null) { street = new ArrayList<String>(); } return this.street; } // 因为street定义成List,缺省设置List没有实现,需要手工添加 public void setStreet (int _lineN, String _street) { if (street == null) { street = new ArrayList<String>(); street.add(0, _street); } else { street.add(_lineN, _street); } } |
BookOrderResponse.java |
protected List<String> returninfo; public List<String> getReturninfo() { if (returninfo == null) { returninfo = new ArrayList<String>(); } return this.returninfo; } // 返回信息同样也是List实现 public void setReturninfo (int _lineN, String _info) { if (returninfo == null) { returninfo = new ArrayList<String>(); returninfo.add(0, _info); } else { returninfo.add(_lineN, _info); } } |
Address.java |
protected XMLGregorianCalendar orderDate; public XMLGregorianCalendar getOrderDate() { return orderDate; } public void setOrderDate(XMLGregorianCalendar value) { this.orderDate = value; } |
OrderBook服务的实现
OrderBookImpl.java OrderBook服务的实现流程:获得Web Service的所有请求信息, BookOrder Schema,并且返回相关信息 |
package org.example.cxfstp04;
import java.util.logging.Logger; import javax.jws.WebMethod; import javax.jws.WebResult;
@javax.jws.WebService(name = "OrderBook", serviceName = "cxfstp04Service", portName = "OrderBookPort", targetNamespace = "http://www.example.org/cxfstp04/", wsdlLocation = "file:/D:/2007/CodeWorm/WebService/SourceCode/CXF/cxfstp03/wsdl/cxfstp04.wsdl" , endpointInterface = "org.example.cxfstp04.OrderBook")
public class OrderBookImpl implements OrderBook {
private static final Logger LOG = Logger.getLogger(OrderBookImpl.class.getName()); public org.example.cxfstp04.BookOrderResponse orderBookOper(org.example.cxfstp04.BookOrder parameters) { LOG.info("Executing operation orderBookOper"); System.out.println(parameters); try { //创建BookOrderResponse返回对象 //采用ObjectFactory方法 org.example.cxfstp04.ObjectFactory _objfactory = new org.example.cxfstp04.ObjectFactory(); org.example.cxfstp04.BookOrderResponse _return = _objfactory.createBookOrderResponse(); _return.setReturn("OK"); //设置返回值的状态为”OK” _return.setReturn("OK"); //获得Web Service请求中的AccountName和AccountNumber信息 String _info_0 = "AccountName is: '" + parameters.getAccountName()+ "' and AccountNumber is : '" +parameters.getAccountNumber()+ "'"; //获得ship地址信息 org.example.cxfstp04.UKAddress _shipAddress = ((org.example.cxfstp04.UKAddress)(parameters.shipAddress)); String _info_1 = _shipAddress.getName()+" "+ _shipAddress.getCity()+" "+ _shipAddress.getCountry() +" "+ _shipAddress.getPostcode(); String _shipstreet = ""; for (int _i=0; _i<_shipAddress.getStreet().size(); _i++) _shipstreet = _shipstreet +" "+ _shipAddress.getStreet().get(_i); //获得billing地址信息 org.example.cxfstp04.BriefUSAddress _billAddress = ((org.example.cxfstp04.BriefUSAddress)(parameters.billAddress)); String _info_2 = _billAddress.getName()+" "+_billAddress.getZip(); String _billstreet =""; for (int _i=0; _i<_billAddress.getStreet().size(); _i++) _billstreet += _billAddress.getStreet().get(_i); //获得书的信息 String _info_3 = "BOOK Name, Quantity and Wholesale-prise is: '" + parameters.getBook().getTitle() + " , " + parameters.getBook().getQuantity() + " , " + parameters.getBook().getWholesalePrice()+ "'"; //获得书总价信息 String _info_4 = "Total " + parameters.getTotal(); //获得订单时间信息 String _info_5 = "The Orderdate is: " + parameters.getOrderDate().getYear()+" , "+parameters.getOrderDate().getMonth()+" , "+parameters.getOrderDate().getDay(); //设置所有返回信息 _return.setReturninfo(0, _info_0); _return.setReturninfo(1, "shiping address is :" + _info_1+_shipstreet); _return.setReturninfo(2, "biliing address is :" + _info_2+_billstreet); _return.setReturninfo(3, _info_3); _return.setReturninfo(4, _info_4); _return.setReturninfo(5, _info_5); return _return; } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } } } |
测试的客户代码
OrderBook_OrderBookPort_Client.java |
package org.example.cxfstp04;
import java.io.File; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar;
public final class OrderBook_OrderBookPort_Client { private static final QName SERVICE_NAME = new QName("http://www.example.org/cxfstp04/", "cxfstp04Service"); private OrderBook_OrderBookPort_Client() { } public static void main(String args[]) throws Exception { if (args.length == 0) { System.out.println("please specify wsdl"); System.exit(1); } URL wsdlURL = null; File wsdlFile = new File(args[0]); try { if (wsdlFile.exists()) { wsdlURL = wsdlFile.toURL(); } else { wsdlURL = new URL(args[0]); } } catch (MalformedURLException e) { e.printStackTrace(); }
Cxfstp04Service ss = new Cxfstp04Service(wsdlURL, SERVICE_NAME); OrderBook port = ss.getOrderBookPort();
System.out.println("Invoking orderBookOper..."); // Add By bldmickey org.example.cxfstp04.BookOrder _orderBookOper_parameters = new org.example.cxfstp04.BookOrder(); // 设置帐号名和帐号 _orderBookOper_parameters.setAccountName("Amazon.com"); _orderBookOper_parameters.setAccountNumber(923); // 设置Ship地址和Billing地址 org.example.cxfstp04.UKAddress _ship_address = new org.example.cxfstp04.UKAddress(); org.example.cxfstp04.BriefUSAddress _bill_address = new org.example.cxfstp04.BriefUSAddress(); _ship_address.setName("Amazon.co.uk"); //因为街道可以有多行,代码生成的是字符串List _ship_address.setStreet(0,"line1: Ridgmont Road"); _ship_address.setStreet(1,"line2: Ridgmont Road"); _ship_address.setCity("Bedford"); _ship_address.setCountry("United Kingdom"); _ship_address.setPostcode("MK43 0ZA"); _bill_address.setName("Amazon.com"); _bill_address.setStreet(0,"line1: 1516 2nd Ave"); _bill_address.setStreet(1,"line2: 1516 2nd Ave"); _bill_address.setStreet(2,"line3: 1516 2nd Ave"); _bill_address.setZip("90952"); // 设置图书信息 org.example.cxfstp04.Book _book = new org.example.cxfstp04.Book(); _book.setTitle("Java Web Services"); _book.setQuantity(300); _book.setWholesalePrice(24.99f); _orderBookOper_parameters.setShipAddress(_ship_address); _orderBookOper_parameters.setBillAddress(_bill_address); _orderBookOper_parameters.setBook(_book); _orderBookOper_parameters.setTotal(8997.00f); // 对于时间JAXB缺省绑定成XMLGregorianCalendar类型,参见 //http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/datatype/XMLGregorianCalendar.html XMLGregorianCalendar _orderdate= DatatypeFactory.newInstance().newXMLGregorianCalendar(); _orderdate.setYear(2007); _orderdate.setMonth(9); _orderdate.setDay(8); _orderdate.setTimezone(0); _orderBookOper_parameters.setOrderDate(_orderdate);
org.example.cxfstp04.BookOrderResponse _orderBookOper__return = port.orderBookOper(_orderBookOper_parameters); System.out.println("The Status of Return is : " + _orderBookOper__return.getReturn()); for (int _i=0; _i<_orderBookOper__return.getReturninfo().size(); _i++) System.out.println("Return info " + _i +" is: " + _orderBookOper__return.getReturninfo().get(_i)); //System.out.println("orderBookOper.result=" + _orderBookOper__return);
System.exit(0); }
} |
简单测试
运行测试服务器:
- 菜单选择Run->Open Run Dialog…->选择Java Application ->选择OrderBookServer_server_cxfstp04->Main Class设置为: org.example.cxfstp04.OrderBook_OrderBookPort_Server->选择Run
运行服务器后显示类似如下:
…… INFO: Started SelectChannelConnector @ 0.0.0.0:9090 Server ready... |
运行测试客户端
- 菜单选择Run->Open Run Dialog…->选择Java Application ->选择OrderBookClient_client_cxfstp04->Main Class设置为: org.example.cxfstp04.OrderBook_OrderBookPort_Client->选择Run
运行测试客户端后显示如下:
INFO: Creating Service {http://www.example.org/cxfstp04/}cxfstp04Service from WSDL: file:/D:/2007/CodeWorm/WebService/SourceCode/CXF/cxfstp03/wsdl/cxfstp04.wsdl Invoking orderBookOper... The Status of Return is : OK Return info 0 is: AccountName is: 'Amazon.com' and AccountNumber is : '923' Return info 1 is: shiping address is :Amazon.co.uk Bedford United Kingdom MK43 0ZA line1: Ridgmont Road line2: Ridgmont Road Return info 2 is: biliing address is :Amazon.com 90952line1: 1516 2nd Aveline2: 1516 2nd Aveline3: 1516 2nd Ave Return info 3 is: BOOK Name, Quantity and Wholesale-prise is: 'Java Web Services , 300 , 24.99' Return info 4 is: Total 8997.0 Return info 5 is: The Orderdate is: 2007 , 9 , 8
|
TcpMonitor
- 运行tcpmonitor
设置目的服务地址和端口号loclhost:9090(服务器)
设置监听的端口为8888
- 运行客户端
修改测试客户端的wsdl参数. 客户端测试代码中,从命令行参数中获得wsdl文件的路径,从中获得请求服务的信息
OrderBook_OrderBookPort_Client.java |
wsdlURL = new URL(args[0]); Cxfstp04Service ss = new Cxfstp04Service(wsdlURL, SERVICE_NAME); |
修改wsdl中的服务地址,从原来的localhost:9090改成localhost:8888,意思将服务请求发送到tcpmonitor的8888端口,由tcpmonitor转发给9090
cxfstp04_test.wsdl |
<wsdl:service name="cxfstp04Service"> <wsdl:port name="OrderBookPort" binding="ns1:cxfstp04ServiceBinding"> <soap:address location="http://localhost:8888/OrderBook"/> </wsdl:port> </wsdl:service> |
菜单选择Run->Open Run Dialog…->选择Java Application ->选择OrderBookClient_client_cxfstp04_testing->Main Class设置为: org.example.cxfstp04.OrderBook_OrderBookPort_Client->在参数中设置程序的参数为: 修改后的cxfstp04_test.wsdl->选择Run
请求和返回
- TCPMonitor返回的结果
省略 |
省略 |
服务部署
详细参见:<< Eclipse Apache CXF集成.doc>>
- 启动本地的tomcat服务器
- 菜单选择File->New->Other…->选择SOA Tools->Deployment Profile后选择Next;
- 在scxstp04中点击wsdl,在File Name中输入:scxstp04Deploy,如下图:选择Next;
- 在Deployment Description选择继续;Package中选择继续;Target Server选择继续;Summary中选择Finish
- 在wsdl目录下将创建scxstp04Deploy.deploy项,右边显示deploy文件,在文件显示框中选择Configuration Tab
- 选择Add Target…->在弹出对话框中选择刚才启动的“Tomcat v5.5 Server at localhost”,选择OK,在Server框中将显示刚才添加的target server。
- 选择Create Target
- 选择Deploy Package
- 查看Console Tab,将显示Package Deploy的状态(类似下面的log)
INFO: Creating Service {http://www.example.org/cxfstp04/}cxfstp04Service from WSDL: WEB-INF/wsdl/cxfstp04.wsdl |
客户端测试
- Web 测试wsdl
浏览器中输入: http://localhost:8081/cxfstp04/services/cxfstp04?wsdl
- 考虑到目前tomcat监听在8081端口,所以修改测试wsdl文件
cxfstp04_testtomcat.wsdl |
<wsdl:service name="cxfstp04Service"> <wsdl:port name="OrderBookPort" binding="ns1:cxfstp04ServiceBinding"> <soap:address location="http://localhost:8081/cxfstp04/services/cxfstp04"/> </wsdl:port> </wsdl:service> |
- 运行客户端,输出结果
省略 |
部署到生产服务器
- Ftp服务cxfstp04.war到$TOMCAT_HOME/webapps目录下
- 浏览器测试http://hostname:port/cxfstp04/services/cxfstp04?wsdl
- 修改客户端wsdl后再测试