JAVA的 WebService规范 JAX-WS实现例子,webservicejax-ws,Web 服务分为Serv
JAVA的 WebService规范 JAX-WS实现例子,webservicejax-ws,Web 服务分为Serv
Web 服务分为Server、Client 两部分,Server 公开Web 服务,Client 调用Web 服务,JAX-WS 的服务端、客户端双方传输数据使用的SOAP 消息格式封装数据,在后面我们会看到其实SOAP 信封内包装的就是一段XML 代码.
I.服务端示例:
我们先看一个服务器端示例:
(1)公开Web 服务的接口IHelloService:
package cn.outofmemory.JASWS; import javax.jws.WebService; @WebService public interface IHelloService { Customer selectMaxAgeStudent(Customer c1, Customer c2); Customer selectMaxLongNameStudent(Customer c1, Customer c2); }
我们看到这个接口很简单,仅仅是使用类级别注解@WebService 就标注了这个接口的方法将公开为Web 服务,使用了这个注解的接口的所有方法都将公开为Web 服务的操作,如果你想屏蔽某个方法,可以使用方法注解@Method 的exclude=true.我们也通常把公开为Web服务的接口叫做SEI(Service EndPoint Interface)服务端点接口.
(2)实现类HelloServiceImpl:
package cn.outofmemory.JASWS; import javax.jws.WebService; @WebService public class HelloServiceImpl implements IHelloService { @Override public Customer selectMaxAgeStudent(Customer c1, Customer c2) { if (c1.getBirthday().getTime() > c2.getBirthday().getTime()) return c2; else return c1; } @Override public Customer selectMaxLongNameStudent(Customer c1, Customer c2) { if (c1.getName().length() > c2.getName().length()) return c1; else return c2; } }
这个实现类没有任何特殊之处,但是如果你的实现类还实现了其他的接口,那么你需要在实现类上使用@WebService 注解的endpointInterface 属性指定那个接口是SEI(全类名).
(3)Customer 类:
package cn.outofmemory.JASWS; import java.util.Date; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Customer") public class Customer { private long id; private String name; private Date birthday; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
这个类是公开为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 服务:
package cn.outofmemory.JASWS; import javax.xml.ws.Endpoint; public class SoapServer { public static void main(String[] args) { Endpoint.publish("http://127.0.0.1:8080/helloService",new HelloServiceImpl()); } }
注意我们发布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:
<?xml version="1.0" encoding="UTF-8"?> <!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> <!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> -<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://byrx.net/JASWS/" name="HelloServiceImplService"> -<types> -<xsd:schema> <xsd:import namespace="http://byrx.net/JASWS/" schemaLocation="http://127.0.0.1:8080/helloService?xsd=1" /> </xsd:schema> </types> -<message name="selectMaxLongNameStudent"> <part name="parameters" element="tns:selectMaxLongNameStudent" /> </message> -<message name="selectMaxLongNameStudentResponse"> <part name="parameters" element="tns:selectMaxLongNameStudentResponse" /> </message> -<message name="selectMaxAgeStudent"> <part name="parameters" element="tns:selectMaxAgeStudent" /> </message> -<message name="selectMaxAgeStudentResponse"> <part name="parameters" element="tns:selectMaxAgeStudentResponse" /> </message> -<portType name="HelloServiceImpl"> -<operation name="selectMaxLongNameStudent"> <input wsam:Action="http://JASWS/HelloServiceImpl/selectMaxLongNameStudentRequest" message="tns:selectMaxLongNameStudent" /> <output wsam:Action="http://JASWS/HelloServiceImpl/selectMaxLongNameStudentResponse" message="tns:selectMaxLongNameStudentResponse" /> </operation> -<operation name="selectMaxAgeStudent"> <input wsam:Action="http://JASWS/HelloServiceImpl/selectMaxAgeStudentRequest" message="tns:selectMaxAgeStudent" /> <output wsam:Action="http://JASWS/HelloServiceImpl/selectMaxAgeStudentResponse" message="tns:selectMaxAgeStudentResponse" /> </operation> </portType> -<binding name="HelloServiceImplPortBinding" type="tns:HelloServiceImpl"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> -<operation name="selectMaxLongNameStudent"> <soap:operation soapAction="" /> -<input> <soap:body use="literal" /> </input> -<output> <soap:body use="literal" /> </output> </operation> -<operation name="selectMaxAgeStudent"> <soap:operation soapAction="" /> -<input> <soap:body use="literal" /> </input> -<output> <soap:body use="literal" /> </output> </operation> </binding> -<service name="HelloServiceImplService"> -<port name="HelloServiceImplPort" binding="tns:HelloServiceImplPortBinding"> <soap:address location="http://127.0.0.1:8080/helloService" /> </port> </service> </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.javaHelloServiceImplService.javaIHelloService.javaObjectFactory.javapackage-info.javaSelectMaxAgeStudent.javaSelectMaxAgeStudentResponse.javaSelectMaxLongNameStudent.javaSelectMaxLongNameStudentResponse.java其中package-info.java、ObjectFactory.java 是JAXB需要的文件;HelloServiceImplService.java继承自javax.xml.ws.Service 类,用于提供WSDL 的客户端视图,里面使用的是大量javax.xml.ws.*包中的注解;剩下的类是Web 服务的接口、方法参数、响应值的类.
在 CXF 中使用JaxWsProxyFactoryBean 客户端代理工厂调用Web 服务,代码如下所示:
package cn.outofmemory.JASWS; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; public class SoapClient { public static void main(String[] args) throws ParseException { JaxWsProxyFactoryBean soapFactoryBean = new JaxWsProxyFactoryBean(); soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService"); soapFactoryBean.setServiceClass(IHelloService.class); Object o = soapFactoryBean.create(); IHelloService helloService = (IHelloService) o; Customer c1 = new Customer(); c1.setId(1); c1.setName("A"); GregorianCalendar calendar = (GregorianCalendar) GregorianCalendar .getInstance(); calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1989-01-28")); c1.setBirthday(new Date("2013-01-01")); Customer c2 = new Customer(); c2.setId(2); c2.setName("B"); calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1990-01-28")); c2.setBirthday(new Date("2013-02-01")); System.out.println(helloService.selectMaxAgeStudent(c1,c2).getName()); } }
用户点评