简单WebService规范 JAX-WS实现例子
Web 服务分为Server、Client 两部分,Server 公开Web 服务,Client 调用Web 服务,JAX-WS 的服务端、客户端双方传输数据使用的SOAP 消息格式封装数据,在后面我们会看到其实SOAP 信封内包装的就是一段XML 代码。
I.服务端示例:
我们先看一个服务器端示例:
(1.)公开Web 服务的接口IHelloService:
1 import javax.jws.WebService; 2 @WebService 3 public interface IHelloService { 4 Customer selectMaxAgeStudent(Customer c1, Customer c2); 5 Customer selectMaxLongNameStudent(Customer c1, Customer c2); 6 }
我们看到这个接口很简单,仅仅是使用类级别注解@WebService 就标注了这个接口的方法将公开为Web 服务,使用了这个注解的接口的所有方法都将公开为Web 服务的操作,如果你想屏蔽某个方法,可以使用方法注解@Method 的exclude=true.我们也通常把公开为Web服务的接口叫做SEI(Service EndPoint Interface)服务端点接口。
(2.)实现类HelloServiceImpl:
1 import javax.jws.WebService; 2 @WebService 3 public class HelloServiceImpl implements IHelloService { 4 @Override 5 public Customer selectMaxAgeStudent(Customer c1, Customer c2) { 6 if (c1.getBirthday()。getTime() > c2.getBirthday()。getTime()) 7 return c2; 8 else 9 return c1; 10 } 11 @Override 12 public Customer selectMaxLongNameStudent(Customer c1, Customer c2) 13 { 14 if (c1.getName()。length() > c2.getName()。length()) 15 return c1; 16 else 17 return c2; 18 } 19 }
这个实现类没有任何特殊之处,但是如果你的实现类还实现了其他的接口,那么你需要在实现类上使用@WebService 注解的endpointInterface 属性指定那个接口是SEI(全类名)。
(3.)Customer 类:
1 import java.util.Date; 2 import javax.xml.bind.annotation.XmlRootElement; 3 @XmlRootElement(name = "Customer") 4 public class Customer { 5 private long id; 6 private String name; 7 private Date birthday; 8 public long getId() { 9 return id; 10 } 11 public void setId(long id) { 12 this.id = id; 13 } 14 public String getName() { 15 return name; 16 } 17 public void setName(String name) { 18 this.name = name; 19 } 20 public Date getBirthday() { 21 return birthday; 22 } 23 public void setBirthday(Date birthday) { 24 this.birthday = birthday; 25 } 26 }
这个类是公开为Web 服务的接口中的参数类型和返回值,因此你需要使用JAXB 注解告诉CXF 如何在XML和Java Object 之间处理,因为前面说过SOAP 消息格式包装的是一段XML代码,那么无论是服务器端还是客户端在接收到SOAP 消息时都需要将XML 转化为JavaObject,在发送SOAP 消息时需要将Java Object 转化为XML.
JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到 XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地 结合XML数据和处理函数。
JDK中JAXB相关的重要Annotation:
@XmlType,将Java类或枚举类型映射到XML模式类型
@XmlAccessorType(XmlAccessType.FIELD)
,控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标
注)字段到XML.其他值还有XmlAccessType.PROPERTY和XmlAccessType.NONE.
@XmlAccessorOrder,控制JAXB 绑定类中属性和字段的排序。
@XmlJavaTypeAdapter,使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML.
@XmlElementWrapper ,对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。
@XmlRootElement,将Java类或枚举类型映射到XML元素。
@XmlElement,将Java类的一个属性映射到与属性同名的一个XML元素。
@XmlAttribute,将Java类的一个属性映射到与属性同名的一个XML属性。
其他:
对于要序列化(marshal)为XML的Java类,绝不能把成员变量声明为public,否则运行将抛出异常com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException.
对于JAXB相关的重要Annotation的声明,如@Xml……,可以放在成员变量的setter()或getter()方法上,两者中任选其一即
可,但决不能放在成员变量上,否则运行将抛出异常
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException.
(4.)发布Web 服务:
1 import javax.xml.ws.Endpoint; 2 public class SoapServer { 3 public static void main(String[] args) { 4 Endpoint.publish("http://127.0.0.1:8080/helloService",new HelloServiceImpl()); 5 } 6 }
注意我们发布Web 服务使用的是javax.xml.ws.*包中的EndPoint 的静态方法publish()。
(5.)查看WSDL:
我们访问http://127.0.0.1:8080/helloService?wsdl
地址,您会看到很长的XML 文件(由于浏览器的问题,如果你看到的是空白页面,请查看源代码),这就是WSDL(WebService
DefinitionLanguage),对于你要访问的Web 服务,只要在其地址后加上,就可以在浏览器中查看用于描述Web
服务的WSDL,这也是一种XML,Web 服务能够被各种编程语言书写的程序访问就是通过WSDL
这种通用的契约来完成的。如果你已经看到WSDL,那么表示我们的Web 服务发布成功了。你可能会差异,我们没有
借助Tomcat 这样的Web 服务器,直接运行一个main 方法是怎么发布的Web 服务呢?其实CXF 内置了Jetty(Servlet 容器),因此你不需要将你的程序部署到Tomcat 等Web 服务器也可以正常发布Web 服务。
(6)。返回的WSDL:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 3 Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. 4 --> 5 <!-- 6 Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. 7 --> 8 -<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://JASWS/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://JASWS/" name="HelloServiceImplService"> 9 -<types> 10 -<xsd:schema> 11 <xsd:import namespace="http://JASWS/" schemaLocation="http://127.0.0.1:8080/helloService?xsd=1" /> 12 </xsd:schema> 13 </types> 14 -<message name="selectMaxLongNameStudent"> 15 <part name="parameters" element="tns:selectMaxLongNameStudent" /> 16 </message> 17 -<message name="selectMaxLongNameStudentResponse"> 18 <part name="parameters" element="tns:selectMaxLongNameStudentResponse" /> 19 </message> 20 -<message name="selectMaxAgeStudent"> 21 <part name="parameters" element="tns:selectMaxAgeStudent" /> 22 </message> 23 -<message name="selectMaxAgeStudentResponse"> 24 <part name="parameters" element="tns:selectMaxAgeStudentResponse" /> 25 </message> 26 -<portType name="HelloServiceImpl"> 27 -<operation name="selectMaxLongNameStudent"> 28 <input wsam:Action="http://JASWS/HelloServiceImpl/selectMaxLongNameStudentRequest" message="tns:selectMaxLongNameStudent" /> 29 <output wsam:Action="http://JASWS/HelloServiceImpl/selectMaxLongNameStudentResponse" message="tns:selectMaxLongNameStudentResponse" /> 30 </operation> 31 -<operation name="selectMaxAgeStudent"> 32 <input wsam:Action="http://JASWS/HelloServiceImpl/selectMaxAgeStudentRequest" message="tns:selectMaxAgeStudent" /> 33 <output wsam:Action="http://JASWS/HelloServiceImpl/selectMaxAgeStudentResponse" message="tns:selectMaxAgeStudentResponse" /> 34 </operation> 35 </portType> 36 -<binding name="HelloServiceImplPortBinding" type="tns:HelloServiceImpl"> 37 <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> 38 -<operation name="selectMaxLongNameStudent"> 39 <soap:operation soapAction="" /> 40 -<input> 41 <soap:body use="literal" /> 42 </input> 43 -<output> 44 <soap:body use="literal" /> 45 </output> 46 </operation> 47 -<operation name="selectMaxAgeStudent"> 48 <soap:operation soapAction="" /> 49 -<input> 50 <soap:body use="literal" /> 51 </input> 52 -<output> 53 <soap:body use="literal" /> 54 </output> 55 </operation> 56 </binding> 57 -<service name="HelloServiceImplService"> 58 -<port name="HelloServiceImplPort" binding="tns:HelloServiceImplPortBinding"> 59 <soap:address location="http://127.0.0.1:8080/helloService" /> 60 </port> 61 </service> 62 </definitions>
下一节来分析该WSDL的结构和组成元素。
(7)。 客户端调用程序
我们从上面可以知道Web 服务只向客户端暴漏WSDL,那么客户端必须将WSDL
转换为自己的编程语言书写的代码。JAX-WS 的各种实现都提供相应的工具进行WSDL 与JAVA 之间的互相转换,你可以在CXF
的运行包中找到bin 目录,其中的wsdl2java.bat 可以将WSDL转换为JAVA 类,bin 目录的各种bat
的名字可以很容易知道其作用,但要注意JAVA 类转换为WSDL 最好使用前面的URL?wsdl
的方式获得,因为这样得到的是最准确的。你可以在命令行将当前目录切换到CXF 的bin 目录,然后运行wsdl2java –h
查看这个批处理命令的各个参数的作用,常用的方式就是 wsdljava –p 包路径 –d 目标文件夹 wsdl 的
url地址。现在我们将前面的WSDL生成客户端代码:
wsdl2java -p net.ilkj.soap.client –d E:\ http://127.0.0.1:8080/helloService?wsdl
你会在E 盘根目录找到生成的客户端代码,然后将它复制到Eclipse 工程即可使用。
如果你使用MyEclipse,可以按照如下步骤从WSDL 生成客户端代码:
New--->Other--->MyEclipse--->Web Services--->Web Services
Client,然后依据设置向导即可完成,但最好还是使用CXF 的wsdl2java
来完成,因为CXF2.2+版本开始支持JAX-WS2.1规范,而MyEclipse 自带的好像是XFire
的wsdl2java,生成的客户端代码可能不是最新规范的。
我们上面的WSDL 会生成如下所示的客户端代码:
Customer.java
HelloServiceImplService.java
IHelloService.java
ObjectFactory.java
package-info.java
SelectMaxAgeStudent.java
SelectMaxAgeStudentResponse.java
SelectMaxLongNameStudent.java
SelectMaxLongNameStudentResponse.java
其中package-info.java、ObjectFactory.java
是JAXB需要的文件;HelloServiceImplService.java继承自javax.xml.ws.Service
类,用于提供WSDL 的客户端视图,里面使用的是大量javax.xml.ws.*包中的注解;剩下的类是Web 服务的接口、方法参数、响应值的类。
在 CXF 中使用JaxWsProxyFactoryBean 客户端代理工厂调用Web 服务,代码如下所示:
1 import java.text.ParseException; 2 import java.text.SimpleDateFormat; 3 import java.util.Date; 4 import java.util.GregorianCalendar; 5 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; 6 public class SoapClient { 7 public static void main(String[] args) throws ParseException { 8 JaxWsProxyFactoryBean soapFactoryBean = new JaxWsProxyFactoryBean(); 9 soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService"); 10 soapFactoryBean.setServiceClass(IHelloService.class); 11 Object o = soapFactoryBean.create(); 12 IHelloService helloService = (IHelloService) o; 13 Customer c1 = new Customer(); 14 c1.setId(1); 15 c1.setName("A"); 16 GregorianCalendar calendar = (GregorianCalendar) 17 GregorianCalendar 18 .getInstance(); 19 calendar.setTime(new SimpleDateFormat("yyyy-MM-dd")。parse("1989-01-28")); 20 c1.setBirthday(new Date("2013-01-01")); 21 Customer c2 = new Customer(); 22 c2.setId(2); 23 c2.setName("B"); 24 calendar.setTime(new SimpleDateFormat("yyyy-MM-dd")。parse("1990-01-28")); 25 c2.setBirthday(new Date("2013-02-01")); 26 System.out.println(helloService.selectMaxAgeStudent(c1,c2)。getName()); 27 } 28 }
浙公网安备 33010602011771号