分享

java6开发webservice绝对好文(转)

 hh3755 2011-11-04
这个系列文章写得真的很不错,由浅入深为表尊重请直接访问原文。

JAVA6开发WebService (二)——JAX-WS例子

JAVA6开发WebService (三)——几个概念

JAVA6开发WebService (四)——SAAJ调用WebService


下面的复制内容仅作以后可能的找不到和知识管理的用途。

 WebService是SOA的一种较好的实现方式,它将应用程序的不同功能单元通过中立的契约(独立于硬件平台、操作系统和编程语言)联系起来,使得各种形式的功能单元更好的集成。


W3C对他的定义是:

    A Web service is a software system designed to support interoperable machine-to-machine interaction over a network. It has an interface described in a machine-processable format (specifically WSDL). Other systems interact with the Web service in a manner prescribed by its description using SOAP messages......"

    Web service是一个软件系统,为了支持跨网络的机器之间相互操作交互而设计。它有一个机器可识别的描述格式(特别是WSDL)。不同的系统之间可以通过SOAP消息在规定的方式下相互调用。(英文不好,请指正!)



    简单的说,WebService是一种独立于特定语言、特定平台,基于网络的、分布式的模块化组件。是一个能够使用xml消息通过网络来访问的Interface,这个Interface描述了一组可访问的操作。


WebService一般分为两种:

    REST式WebService,基于HTTP协议;

    RPC式WebService,基于SOAP协议,不过SOAP也是基于HTTP传输的。

  狭义上的WebService是指第二种RPC式的WebService,也就是我们常说的那种。


JAVA中有三种WebService规范,分别是JAX-WS(JAX-RPC)、JAX-RS、JAXM&SAAJ。


    这里先说JAX-WS(Java API For XML-WebService),JDK1.6 自带的版本为JAX-WS2.1,其底层支持为JAXB。早期的JAVA Web服务规范JAX-RPC(Java API ForXML-Remote Procedure Call)目前已经被JAX-WS 规范取代,JAX-WS 是JAX-RPC 的演进版本,但JAX-WS 并不完全向后兼容JAX-RPC。


    废话不多说了,先来写一个最简单的例子:


服务器端:

    在想要发布为WebService的类上加上注解@WebService,这个类的方法就变为WebService的方法了,再通过Endpoint的publish方法,发布这个服务,到此,一个最简单的WebService搞定。运行main方法,在浏览器里输入”http://localhost:8080/com.why.webservice.Hello?wsdl  会看到你的WSDL信息。


    不过需要注意一 下, 有的同学如果不加@SOAPBinding(style = SOAPBinding.Style.RPC)这行代码会报错:

com.sun.xml.internal.ws.model.RuntimeModelerException: runtime modeler error: Wrapper class com.why.webservice.jaxws.SayHello is not found. Have you run APT to generate them?

网上资料说只要将JDK升级到1.6u17就可以了,我直接升级到了1.6u22(1.6.0_22-b04),问题解决!

 

Java代码  收藏代码
  1. package com.why.webservice;  
  2.   
  3. import javax.jws.WebService;  
  4. import javax.xml.ws.Endpoint;  
  5.   
  6. /** 
  7.  *  
  8.  * @author why 
  9.  * 
  10.  */  
  11. @WebService  
  12. public class Hello {  
  13.   
  14.     public String sayHello(String name) {  
  15.         return "Hello " + name;  
  16.     }  
  17.   
  18.     public static void main(String[] args){  
  19.         Endpoint.publish("http://localhost:8080/com.why.webservice.Hello"new Hello());  
  20.         System.out.println("Success");  
  21.     }  
  22. }  
 

客户端:

    在命令行输入命令 wsimport -p [包名] -keep [发布的服务地址?wsdl] 生成客户端代码,如生成本例的客户端代码”wsimport -p com.why.client -keep http://localhost:8080/com.why.webservice.Hello?wsdl“,当然,前提是你已经配好了JAVA环境变量。控制台会显示



利用这些生成的客户端代码,就可以调用这个WebService服务了:

 

Java代码  收藏代码
  1. package com.why.client;  
  2.   
  3. /** 
  4.  *  
  5.  * @author why 
  6.  * 
  7.  */  
  8. public class HelloClient {  
  9.   
  10.     /** 
  11.      * @param args 
  12.      */  
  13.     public static void main(String[] args) {  
  14.         Hello hello = new HelloService().getHelloPort();  
  15.         String s = hello.sayHello("why");  
  16.         System.out.println(s);  
  17.     }  
  18. }  

 

执行代码,输出:Hello why

2。

  上一篇写了个最简单的小例子,只是为了说明JAVA6开发Web Service很方便,这一篇稍微深入一点,写个稍微有点代表性的小例子。

 

    依然使用 JAX-WS(jdk自带的实现)方式,这次要在服务中使用一个复杂类型Customer,并实现附件传输的功能,这里使用MTOM的附件传输方式。MTOM(SOAP Message Transmission Optimization Mechanism)是SOAP 消息传输优化机制,MTOM可以在SOAP 消息中发送二进制数据。

 

先来看Customer类:

 

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.activation.DataHandler;  
  6. import javax.xml.bind.annotation.XmlAccessType;  
  7. import javax.xml.bind.annotation.XmlAccessorType;  
  8. import javax.xml.bind.annotation.XmlMimeType;  
  9. import javax.xml.bind.annotation.XmlRootElement;  
  10.   
  11. @XmlRootElement(name = "Customer")  
  12. @XmlAccessorType(XmlAccessType.FIELD)  
  13. public class Customer {  
  14.     private long id;  
  15.     private String name;  
  16.     private Date birthday;  
  17.     @XmlMimeType("application/octet-stream")  
  18.     private DataHandler imageData;  
  19.       
  20.         //getter and setter  
  21.         ......  
  22. }  

     MTOM 方式中要传输的附件必须使用javax.activation.DataHandler 类,还要注意必须在类上使用@XmlAccessorType(FIELD)注解,标注JAXB 在进行JAVA 对象与XML 之间进行转换时只关注字段,而不关注属性(getXXX()方法),否则发布Web 服务时会报出现了两个imageData 属性的错误,原因未知,可能是BUG。

    然后使用@XmlMimeType 注解标注这是一个附件类型的数据,这里我们标注imageData 是一个二进制文件,当然你也可以使用具体的MIME类型,譬如:image/jpg、image/gif 等,但要考虑到客户端是否支持。

 

接口类:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import javax.jws.WebParam;  
  4. import javax.jws.WebService;  
  5. import javax.xml.ws.soap.MTOM;  
  6.   
  7. /** 
  8.  *  
  9.  * @author why 
  10.  * 
  11.  */  
  12. @WebService(name="Hello")  
  13. @SOAPBinding(style = SOAPBinding.Style.RPC)  
  14. @MTOM  
  15. public interface Hello {  
  16.     public void printContext();  
  17.     public Customer selectCustomerByName(@WebParam(name = "customer")Customer customer);  
  18.     public Customer selectMaxAgeCustomer(Customer c1, Customer c2);  
  19. }  

    @MTOM注解用于开启MTOM功能。

    @WebService注解中的name属性标注在接口类上,可以指定wsdl中接口名称,也就是生成的客户端代码中接口类的名字。

    @SOAPBinding(style = SOAPBinding.Style.RPC)指定SOAP消息样式,有两个枚举值:SOAPBinding.Style.DOCUMENT(默认)和SOAPBinding.Style.RPC,可以对比这两种方式生成的wsdl会有所不同,而且生成的客户端代码也会有所不同。

 

实现类:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.io.OutputStream;  
  8. import java.text.ParseException;  
  9. import java.text.SimpleDateFormat;  
  10. import java.util.Date;  
  11. import java.util.Set;  
  12. import javax.activation.DataHandler;  
  13. import javax.activation.FileDataSource;  
  14. import javax.annotation.Resource;  
  15. import javax.jws.WebService;  
  16. import javax.xml.ws.WebServiceContext;  
  17. import javax.xml.ws.handler.MessageContext;  
  18.   
  19. /** 
  20.  *  
  21.  * @author why 
  22.  * 
  23.  */  
  24. @WebService(serviceName="HelloService",portName="HelloServicePort",targetNamespace="http://service./",endpointInterface="com.why.server.Hello")  
  25. public class HelloImpl implements Hello {  
  26.       
  27.     @Resource  
  28.     private WebServiceContext context;  
  29.       
  30.     @Override  
  31.     public void printContext(){  
  32.         MessageContext ctx = context.getMessageContext();  
  33.         Set<String> set = ctx.keySet();  
  34.         for (String key : set) {  
  35.             System.out.println("{" + key + "," + ctx.get(key) +"}");  
  36.             try {  
  37.                 System.out.println("key.scope=" + ctx.getScope(key));  
  38.             } catch (Exception e) {  
  39.                 System.out.println(key + " is not exits");  
  40.             }  
  41.         }  
  42.     }  
  43.       
  44.     @Override  
  45.     public Customer selectCustomerByName(Customer customer) {  
  46.         if("why".equals(customer.getName())){  
  47.             customer.setId(1);  
  48.             try {  
  49.                 customer.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07"));  
  50.             } catch (ParseException e) {  
  51.                 e.printStackTrace();  
  52.             }  
  53.             customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "why.jpg"))));  
  54.         }else{  
  55.             customer.setId(2);  
  56.             customer.setBirthday(new Date());  
  57.             customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "origin.jpg"))));  
  58.         }  
  59.         return customer;  
  60.     }  
  61.       
  62.     @Override  
  63.     public Customer selectMaxAgeCustomer(Customer c1, Customer c2) {  
  64.         try {  
  65.             // 输出接收到的附件  
  66.             System.out.println("c1.getImageData().getContentType()=" + c1.getImageData().getContentType());  
  67.             InputStream is = c2.getImageData().getInputStream();  
  68.             OutputStream os = new FileOutputStream("c:\\temp1.jpg");  
  69.             byte[] bytes = new byte[1024];  
  70.             int c;  
  71.             while ((c = is.read(bytes)) != -1) {  
  72.                 os.write(bytes, 0, c);  
  73.             }  
  74.             os.close();  
  75.               
  76.             System.out.println("c2.getImageData().getContentType()=" + c2.getImageData().getContentType());  
  77.             is = c2.getImageData().getInputStream();  
  78.             os = new FileOutputStream("c:\\temp2.jpg");  
  79.             bytes = new byte[1024];  
  80.             while ((c = is.read(bytes)) != -1) {  
  81.                 os.write(bytes, 0, c);  
  82.             }  
  83.             os.close();  
  84.         } catch (IOException e) {  
  85.             e.printStackTrace();  
  86.         }  
  87.           
  88.         if (c1.getBirthday().getTime() > c2.getBirthday().getTime()){  
  89.             return c2;  
  90.         }  
  91.         else{  
  92.             return c1;  
  93.         }  
  94.     }  
  95. }  
 

    @WebService注解的serviceName属性指定wsdl中service节点的name属性值。portName属性指定wsdl中service节点下port节点name属性值。targetNamespace属性指定wsdl根节点definitions的targetNamespace属性值。endpointInterface属性指定要发布的WebService接口的全路径名,当实现类实现了多个接口时,需要通过此属性标注哪个类是WebService的服务端点接口(SEI)。

    在这个类中,通过@Resource注解注入了一个WebServiceContext对象,这个对象即是WebService的上下文环境。

 

发布这个服务:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import javax.xml.ws.Endpoint;  
  4.   
  5. /** 
  6.  *  
  7.  * @author why 
  8.  * 
  9.  */  
  10. public class SoapServer {  
  11.     public static void main(String[] args) {  
  12.         Endpoint.publish("http://localhost:8080/helloService",new HelloImpl());  
  13.     }  
  14. }  
 

    在命令行键入“wsimport -p com.why.client -keep http://localhost:8080/helloService?wsdl”生成客户端代码,拷贝到工程相应文件夹里,这时,就可以调用这个服务了:

Java代码  收藏代码
  1. package com.why.client;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.OutputStream;  
  6. import java.net.MalformedURLException;  
  7. import java.net.URL;  
  8. import java.text.ParseException;  
  9. import java.text.SimpleDateFormat;  
  10. import java.util.GregorianCalendar;  
  11. import javax.activation.DataHandler;  
  12. import javax.activation.DataSource;  
  13. import javax.activation.FileDataSource;  
  14. import javax.xml.datatype.DatatypeConfigurationException;  
  15. import javax.xml.datatype.DatatypeFactory;  
  16. import javax.xml.namespace.QName;  
  17.   
  18. /** 
  19.  *  
  20.  * @author why 
  21.  * 
  22.  */  
  23. public class SoapClient {  
  24.     public static void main(String[] args) throws ParseException, MalformedURLException {  
  25.         QName qName = new QName("http://service./","HelloService");  
  26.         HelloService helloService = new HelloService(new URL("http://127.0.0.1:8080/helloService?wsdl"),qName);  
  27.         Hello hello = (Hello) helloService.getPort(Hello.class);  
  28.           
  29.         hello.printContext();  
  30.           
  31.         System.out.println("---------------------------------------------------");  
  32.           
  33.         Customer customer = new Customer();  
  34.         customer.setName("why");  
  35.         DataSource ds = hello.selectCustomerByName(customer).getImageData().getDataSource();  
  36.         String attachmentMimeType = ds.getContentType();  
  37.         System.out.println(attachmentMimeType);  
  38.         try {  
  39.             InputStream is = ds.getInputStream();  
  40.             OutputStream os = new FileOutputStream("c:\\why_temp.jpg");  
  41.             byte[] bytes = new byte[1024];  
  42.             int c;  
  43.             while ((c = is.read(bytes)) != -1) {  
  44.                 os.write(bytes, 0, c);  
  45.             }  
  46.         } catch (IOException e) {  
  47.             e.printStackTrace();  
  48.         }  
  49.           
  50.         System.out.println("########################################");  
  51.           
  52.         Customer c1 = new Customer();  
  53.         c1.setId(1);  
  54.         c1.setName("why");  
  55.         GregorianCalendar calendar = (GregorianCalendar)GregorianCalendar.getInstance();  
  56.         calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07"));  
  57.         try {  
  58.             c1.setBirthday(DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar));  
  59.         } catch (DatatypeConfigurationException e) {  
  60.             e.printStackTrace();  
  61.         }  
  62.         c1.setImageData(new DataHandler(new FileDataSource("c:\\c1.jpg")));  
  63.           
  64.         Customer c2 = new Customer();  
  65.         c2.setId(2);  
  66.         c2.setName("abc");  
  67.         calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1986-10-07"));  
  68.         try {  
  69.             c2.setBirthday(DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar));  
  70.         } catch (DatatypeConfigurationException e) {  
  71.             e.printStackTrace();  
  72.         }  
  73.         c2.setImageData(new DataHandler(new FileDataSource("c:\\c2.jpg")));  
  74.           
  75.         Customer c = hello.selectMaxAgeCustomer(c1,c2);  
  76.         System.out.println(c.getName());  
  77.           
  78.     }  
  79. }  
 

附件是我的工程,当然运行这个程序,需先在C盘建立几个文件c1.jpg、c2.jpg、origin.jpg和why.jpg。

3。

要了解WebService,光能写代码不行啊,这说说WebService最基本的概念。

 

首先WebService要知道几个最基本的概念:


1、XML以及XML Schema

    XML 是Web Service表示数据的基本格式。XML是一套通用的数据表示格式,与平台无关,这就使不同语言构建的系统之间相互传递数据成为可能。

    XML Schema-XSD 拥有一套标准的、可扩展的数据类型系统,Web Service即是用XSD来作为数据类型系统的。由于不同语言之间数据类型也不尽相同,因此,数据传输过程中,必须将其转化为一种通用的数据类型,即XSD的数据类型。

 

2、SOAP

    SOAP(Simple Object Access Protocol),简单对象访问协议,它是基于XML格式的消息交换协议。SOAP定义了一个envelope对象,使用envelope来包装要传递的消息,而消息本身可以采用自身特定的词汇,使用namespace来区分彼此。简单的说,在WebService中传递的东西是一封信,SOAP就是信的通用格式,他定义了一封信应该有信封,信封里装着信的内容,信封(envlope)的格式是固定的,而信的内容(要传递的数据)你可以自己定义。

 

3、WSDL

    WSDL(Web Service Description Language),Web Service描述语言,使用XML语言对Web Service及其函数、参数、返回值、数据类型等信息进行描述。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的。

 

4、UDDI

    UDDI(Universal Description Discovery and Integration),统一描述、发现和集成协议。UDDI 是一种目录服务,企业可以使用它对 Web services 进行注册和搜索。UDDI是一个分布式的互联网服务注册机制,他实现了一组可公开访问的接 口,通过这些接口,网络服务可以向服务信息库注册其服务信息、服务需求者可以找到分散在世界各地的网络服务。UDDI 并不像 WSDL 和 SOAP 一样深入人心,因为很多时候,使用者知道 Web 服务的位置(通常位于公司的企业内部网中)。

 

5、远程过程调用(RPC)与消息传递

    Web service本身实际是在实现应用程序间的通信,实现通信的方式有两种:远程过程调用(RPC)和消息传递(DOCUMENT)。使用RPC的时候,客户端的概念是调用服务器上的远程过程,通常方式为实例化一个远程对象并调用其方法和属性。消息传递的概念是,客户端向服务器发送消息,然后等待服务器的回应。消息传递系统强调的是消息的发送和回应,而不是远程对象的界面。他们最大不同就是RPC不能通过Schema 来校验,而Document 类型是可以的。因此document 类型webservice成为主流 ,Document也是JAX-WS默认的实现方式。

    有一种说法是,Web Service = SOAP + WSDL + HTTP。其中,SOAP协议是web service的主体,它通过HTTP或者SMTP等应用层协议进行通讯,自身使用XML文件来描述程序的函数方法和参数信息,从而完成不同主机的异构系统间的服务处理。这里的WSDL(Web Services Description Language)web 服务描述语言也是一个XML文档,它通过HTTP向公众发布,公告客户端程序关于某个具体的 Web service服务的URL信息、方法的命名,参数,返回值等。

 

SOAP协议简介

    上面说了,SOAP是一种基于XML的消息通讯格式,用于网络上,不同平台,不同语言的应用程序间的通讯。一条 SOAP 消息就是一个普通的 XML 文档,包含下列元素: 
Envelope    标识XML 文档一条 SOAP 消息 
Header       包含头部信息的XML标签 
Body          包含所有的调用和响应的主体信息的标签 
Fault           错误信息标签。


SOAP 消息的基本结构是

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <soap:Envelope xmlns:soap="http://schemas./soap/envelope/">  
  3.     <soap:Header>  
  4.         ...  
  5.     </soap:Header>  
  6.     <soap:Body>  
  7.         ...  
  8.         <soap:Fault>  
  9.             ...  
  10.         </soap:Fault>  
  11.     </soap:Body>  
  12. </soap:Envelope>  
 

soap:Envelope是SOAP中根元素元素。Envelope元素中可以包含多个可选的Header元素,必须同时包含一个Body元素。Header元素必须是Envelope元素的直接子元素,并且要位于Body元素之前。

 

soap:Header与HTTP请求中的Headers类似,用于传送一些基础的通用的数据,例如安全、事务等方面的信息。Header不是SOAP消息中的必需元素,但他是扩展SOAP协议的一个功能非常强大的功能。Header有两个非常重要的属性

 

soap:Body是SOAP消息中必需的元素,用于传送实际的业务数据或错误信息。

 

soap:Fault是soap:Body的子元素,用于传送错误信息,当有错误发生时才需要次标签。

 

可参考:http://askcuix./blog/211005

 

WSDL简介

    WSDL是WebService的描述语言,也是使用xml编写,用于描述、也可定位WebService,还不属于W3C标准。WSDL主要包含以下几个元素:

definitions   WSDL的根节点,主要包括:name属性,WebService服务名,可通过@WebService注解的serviceName                      更改;targetNamespace属性,命名空间,可通过@WebService注解的targetNamespace更改。

types          web service 使用的数据类型,他是独立与机器和语言的类型定义,这些类型被message标签所引用。

message     web service 使用的消息,他定义了WebService函数的参数。在WSDL中,输入输出参数要分开定义,使用                    不同的message标签体标识。message定义的输入输出参数被portType标签所引用。

portType    web service 执行的操作。引用message标签定义的内容来描述函数信息(函数名,输入输出参数等)。
binding       web service 使用的通信协议。将portType中定义的服务绑定到SOAP协议,这部分XML 指定最终发布的                   WebService的SOAP 消息封装格式、发布地址等。

service        这个元素的name 属性指定服务名称(这里与根元素的name 属性相同),子元素port的name 属性指定port                   名称,子元素address的location 属性指定Web 服务的地址。

 

WSDL的基本结构是:

Xml代码  收藏代码
  1. <definitions>  
  2.     <types>  
  3.         ...  
  4.     </types>  
  5.     <message>  
  6.         ...  
  7.     </message>  
  8.     <portType>  
  9.         ...  
  10.     </portType>  
  11.     <binding>  
  12.         ...  
  13.     </binding>  
  14.         <service>  
  15.         ...  
  16.     </service>  
  17. </definitions>  

 

可参考:http://www.w3school.com.cn/wsdl/index.asp

 

    我就不浪费资源粘一大段WSDL和SOAP的实例过来了,上一篇写了个小例子,发布后在浏览器输入“服务地址”+ “?wsdl”就可以查看(如那个例子的WSDL地址是http://127.0.0.1:8080/helloService?wsdl)。SOAP可以使用一个HTTP监听工具查看(我使用的是HTTPAnalyzerFullV5),或者通过CXF的实现发布程序,使用CXF的拦截器可以看到SOAP消息内容。

 

将上一篇的代码更改为CXF实现:

    首先下载CXF的包,我下的是apache-cxf-2.3.0.zip,将lib下的jar包引入工程路径,将发布服务的代码更改为:

Java代码  收藏代码
  1.     public static void main(String[] args) {  
  2. //      Endpoint.publish("http://localhost:8080/helloService",new HelloImpl());  
  3.           
  4.         //使用CXF特有的API---JaxWsServerFactoryBean发布  
  5.         JaxWsServerFactoryBean soapFactoryBean = new JaxWsServerFactoryBean();  
  6.         soapFactoryBean.getInInterceptors().add(new LoggingInInterceptor());  
  7.         soapFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());  
  8.         soapFactoryBean.setServiceClass(HelloImpl.class);  
  9.         soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService");  
  10.         soapFactoryBean.create();  
  11.     }  
  12. }  
 

    这样,就可以在控制台看到SOAP的消息内容了,信息: Inbound Message是服务器端接受到的内容,信息: Outbound Message是服务器返回给客户端的内容。当然,使用CXF的实现方式时,客户端调用也可以使用CXF的特有方式:

Java代码  收藏代码
  1.         //1、使用标准的JAX-WS 的API 完成客户端调用  
  2. //      QName qName = new QName("http://service./","HelloService");  
  3. //      HelloService helloService = new HelloService(new URL("http://127.0.0.1:8080/helloService?wsdl"),qName);  
  4. //      Hello hello = (Hello) helloService.getPort(Hello.class);  
  5.           
  6.         //2、使用了CXF 的JaxWsProxyFactoryBean 来访问Web 服务      
  7.         JaxWsProxyFactoryBean soapFactoryBean = new JaxWsProxyFactoryBean();  
  8.         soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService");  
  9.         soapFactoryBean.setServiceClass(Hello.class);  
  10.         Object o = soapFactoryBean.create();  
  11.         Hello hello = (Hello) o;  
 

    注意,当使用不同的WebService实现时,其生成的WSDL内容可能会稍有差异,但总体上都是一样的。

 

Apache CXF下载地址 :http://cxf./download.html

 

附件是上一篇中例子的工程,有一点点修改,我把lib里的CXF的jar包删了,太大了,不让上传,想看的同学可以到上面的地址下载CXF相应的包,解压后将lib里的东东拷到我工程的lib目录里就OK了,当然还得先在C盘建立几个测试文件c1.jpg、c2.jpg、origin.jpg和why.jpg。

4。

 前面写了个JAX-WS的小例子,看到用JAVA6开发WebService确实很简单,也很方便,不过前面也说了,JAVA有三种WebService规范,JAX-WS是其中一种,现在来看看JAXM&SAAJ。

 

    最近在做一个接口平台的项目,接口嘛,当然得涉及到对WebService的接口了,我们计划做成一个通用的平台,通过配置文件进行配置后就可以动态对某一个接口进行调用,但像前面的例子那样,每次都要生成一堆客户端代码,这可受不了。如果调用的接口唯一,生成一次客户端代码当然没问题,但如果要调用的接口是动态的,这就不好办了。因此,我需要了解SOAP更多底层的细节,由我自己来组织SOAP中的内容而不是完全由代码生成器生成。

 

    仍使用前面例子中的服务器端:

接口:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import javax.jws.WebParam;  
  4. import javax.jws.WebService;  
  5. import javax.jws.soap.SOAPBinding;  
  6. import javax.xml.ws.soap.MTOM;  
  7.   
  8. /** 
  9.  *  
  10.  * @author why 
  11.  * 
  12.  */  
  13. @WebService(name="Hello")  
  14. @SOAPBinding(style = SOAPBinding.Style.RPC)  
  15. public interface Hello {  
  16.     public void printContext();  
  17.     public Customer selectCustomerByName(@WebParam(name = "c",header=true)Customer customer);  
  18.     public Customer selectMaxAgeCustomer(Customer c1, Customer c2);  
  19. }  

实现类:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.io.OutputStream;  
  8. import java.text.ParseException;  
  9. import java.text.SimpleDateFormat;  
  10. import java.util.Date;  
  11. import java.util.Set;  
  12. import javax.activation.DataHandler;  
  13. import javax.activation.FileDataSource;  
  14. import javax.annotation.Resource;  
  15. import javax.jws.WebService;  
  16. import javax.xml.ws.WebServiceContext;  
  17. import javax.xml.ws.handler.MessageContext;  
  18. import javax.xml.ws.soap.MTOM;  
  19.   
  20. /** 
  21.  *  
  22.  * 通过@MTOM注解启动MTOM传输方式,使用CXF实现时,这个注解放在接口或者实现类上都可以,使用JDK1.6自带实现时,需标注在实现类上 
  23.  * @author why 
  24.  * 
  25.  */  
  26. @WebService(serviceName="HelloService",portName="HelloServicePort",targetNamespace="http://service./",endpointInterface="com.why.server.Hello")  
  27. @MTOM  
  28. public class HelloImpl implements Hello {  
  29.       
  30.     @Resource  
  31.     private WebServiceContext context;  
  32.       
  33.     @Override  
  34.     public void printContext(){  
  35.         MessageContext ctx = context.getMessageContext();  
  36.         Set<String> set = ctx.keySet();  
  37.         for (String key : set) {  
  38.             System.out.println("{" + key + "," + ctx.get(key) +"}");  
  39.             try {  
  40.                 System.out.println("key.scope=" + ctx.getScope(key));  
  41.             } catch (Exception e) {  
  42.                 System.out.println(key + " is not exits");  
  43.             }  
  44.         }  
  45.     }  
  46.       
  47.     @Override  
  48.     public Customer selectCustomerByName(Customer customer) {  
  49.         if("why".equals(customer.getName())){  
  50.             customer.setId(1);  
  51.             try {  
  52.                 customer.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07"));  
  53.             } catch (ParseException e) {  
  54.                 e.printStackTrace();  
  55.             }  
  56.             customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "why.jpg"))));  
  57.         }else{  
  58.             customer.setId(2);  
  59.             customer.setBirthday(new Date());  
  60.             customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "origin.jpg"))));  
  61.         }  
  62.         return customer;  
  63.     }  
  64.       
  65.     @Override  
  66.     public Customer selectMaxAgeCustomer(Customer c1, Customer c2) {  
  67.         try {  
  68.             // 输出接收到的附件  
  69.             System.out.println("c1.getImageData().getContentType()=" + c1.getImageData().getContentType());  
  70.             InputStream is = c1.getImageData().getInputStream();  
  71.             OutputStream os = new FileOutputStream("c:\\temp1.jpg");  
  72.             byte[] bytes = new byte[1024];  
  73.             int c;  
  74.             while ((c = is.read(bytes)) != -1) {  
  75.                 os.write(bytes, 0, c);  
  76.             }  
  77.             os.close();  
  78.               
  79.             System.out.println("c2.getImageData().getContentType()=" + c2.getImageData().getContentType());  
  80.             is = c2.getImageData().getInputStream();  
  81.             os = new FileOutputStream("c:\\temp2.jpg");  
  82.             bytes = new byte[1024];  
  83.             while ((c = is.read(bytes)) != -1) {  
  84.                 os.write(bytes, 0, c);  
  85.             }  
  86.             os.close();  
  87.         } catch (IOException e) {  
  88.             e.printStackTrace();  
  89.         }  
  90.           
  91.         if (c1.getBirthday().getTime() > c2.getBirthday().getTime()){  
  92.             return c2;  
  93.         }  
  94.         else{  
  95.             return c1;  
  96.         }  
  97.     }  
  98. }  

Customer类:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.activation.DataHandler;  
  6. import javax.xml.bind.annotation.XmlAccessType;  
  7. import javax.xml.bind.annotation.XmlAccessorType;  
  8. import javax.xml.bind.annotation.XmlMimeType;  
  9. import javax.xml.bind.annotation.XmlRootElement;  
  10.   
  11. /** 
  12.  *  
  13.  * @author why 
  14.  * 
  15.  */  
  16. @XmlRootElement(name = "Customer")  
  17. @XmlAccessorType(XmlAccessType.FIELD)  
  18. public class Customer {  
  19.     private long id;  
  20.     private String name;  
  21.     private Date birthday;  
  22.     @XmlMimeType("application/octet-stream")  
  23.     private DataHandler imageData;  
  24.       
  25.     public long getId() {  
  26.         return id;  
  27.     }  
  28.     public void setId(long id) {  
  29.         this.id = id;  
  30.     }  
  31.     public String getName() {  
  32.         return name;  
  33.     }  
  34.     public void setName(String name) {  
  35.         this.name = name;  
  36.     }  
  37.     public Date getBirthday() {  
  38.         return birthday;  
  39.     }  
  40.     public void setBirthday(Date birthday) {  
  41.         this.birthday = birthday;  
  42.     }  
  43.     public DataHandler getImageData() {  
  44.         return imageData;  
  45.     }  
  46.     public void setImageData(DataHandler imageData) {  
  47.         this.imageData = imageData;  
  48.     }  
  49. }  

发布:

Java代码  收藏代码
  1. package com.why.server;  
  2.   
  3. import javax.xml.ws.Endpoint;  
  4.   
  5. /** 
  6.  *  
  7.  * @author why 
  8.  * 
  9.  */  
  10. public class SoapServer {  
  11.     public static void main(String[] args) {  
  12.         Endpoint.publish("http://localhost:8080/helloService",new HelloImpl());  
  13.   
  14.     }  
  15. }  
 

    这次不生成客户端类,而是通过自己组织SOAP消息,向服务器发送请求。首先,我们需要一个到WebService服务的连接(就像Connection之于JDBC),通过javax.xml.soap.SOAPConnectionFactory的createConnection()可以获得一个WebService连接。获得连接之后,我们就可以组织我们的SOAP消息了。通过javax.xml.soap.MessageFactory的createMessage()方法,获得一个javax.xml.soap.SOAPMessage,SOAPMessage就是我们SOAP消息的入口。我们知道,SOAP其实就是一个XML,有了SOAPMessage这个入口,剩下的就是对XML的组织和解析了。对于SOAP消息的各个部分,SOAPMessage都有对应的接口:

Java代码  收藏代码
  1. // 获取SOAP连接工厂  
  2. SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();  
  3. // 从SOAP连接工厂创建SOAP连接对象  
  4. SOAPConnection connection = factory.createConnection();  
  5. // 获取消息工厂  
  6. MessageFactory mFactory = MessageFactory.newInstance();  
  7. // 从消息工厂创建SOAP消息对象  
  8. SOAPMessage message = mFactory.createMessage();  
  9. // 创建SOAPPart对象  
  10. SOAPPart part = message.getSOAPPart();  
  11. // 创建SOAP信封对象  
  12. SOAPEnvelope envelope = part.getEnvelope();  
  13. // 创建SOAPHeader对象  
  14. SOAPHeader header = message.getSOAPHeader();  
  15. // 创建SOAPBody对  
  16. SOAPBody body = envelope.getBody();  
 

    把我们需要传递的参数组织好,通过connection.call方法进行对WebService的调用,他仍然会给我们返回一个SOAPMessage对象,对应服务器端的三个函数,我分别写了对应的三个方法对其进行调用,以下是我的客户端类:

 

Java代码  收藏代码
  1. package com.why.client;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.InputStream;  
  5. import java.io.OutputStream;  
  6. import java.net.URL;  
  7. import java.util.Iterator;  
  8. import java.util.UUID;  
  9. import javax.activation.DataHandler;  
  10. import javax.activation.FileDataSource;  
  11. import javax.xml.namespace.QName;  
  12. import javax.xml.soap.AttachmentPart;  
  13. import javax.xml.soap.MessageFactory;  
  14. import javax.xml.soap.SOAPBody;  
  15. import javax.xml.soap.SOAPBodyElement;  
  16. import javax.xml.soap.SOAPConnection;  
  17. import javax.xml.soap.SOAPConnectionFactory;  
  18. import javax.xml.soap.SOAPElement;  
  19. import javax.xml.soap.SOAPEnvelope;  
  20. import javax.xml.soap.SOAPHeader;  
  21. import javax.xml.soap.SOAPHeaderElement;  
  22. import javax.xml.soap.SOAPMessage;  
  23. import javax.xml.soap.SOAPPart;  
  24.   
  25. /** 
  26.  *  
  27.  * @author why 
  28.  * 
  29.  */  
  30. public class SoapClient {  
  31.     public static void main(String[] args) throws Exception{  
  32.           
  33.         printContext();  
  34.           
  35.         selectCustomerByName();  
  36.           
  37.         selectMaxAgeCustomer();  
  38.     }  
  39.       
  40.     /** 
  41.      * 调用一个无参函数 
  42.      * @throws Exception 
  43.      */  
  44.     public static void printContext() throws Exception{  
  45.         // 获取SOAP连接工厂  
  46.         SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();  
  47.         // 从SOAP连接工厂创建SOAP连接对象  
  48.         SOAPConnection connection = factory.createConnection();  
  49.         // 获取消息工厂  
  50.         MessageFactory mFactory = MessageFactory.newInstance();  
  51.         // 从消息工厂创建SOAP消息对象  
  52.         SOAPMessage message = mFactory.createMessage();  
  53.         // 创建SOAPPart对象  
  54.         SOAPPart part = message.getSOAPPart();  
  55.         // 创建SOAP信封对象  
  56.         SOAPEnvelope envelope = part.getEnvelope();  
  57.         // 创建SOAPHeader对象  
  58.         SOAPHeader header = message.getSOAPHeader();  
  59.         // 创建SOAPBody对象  
  60.         SOAPBody body = envelope.getBody();  
  61.           
  62.         // 创建XML的根元素  
  63.         SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server./""printContext""ns1"));  
  64.           
  65.         // 访问Web服务地址  
  66.         SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService"));  
  67.         // 控制台输出返回的SOAP消息  
  68.         OutputStream os = System.out;  
  69.         reMessage.writeTo(os);  
  70.           
  71.         connection.close();  
  72.     }  
  73.       
  74.     /** 
  75.      * 调用一个在soap:HEADER中传递参数的函数 
  76.      * @throws Exception 
  77.      */  
  78.     public static void selectCustomerByName() throws Exception{  
  79.         // 获取SOAP连接工厂  
  80.         SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();  
  81.         // 从SOAP连接工厂创建SOAP连接对象  
  82.         SOAPConnection connection = factory.createConnection();  
  83.         // 获取消息工厂  
  84.         MessageFactory mFactory = MessageFactory.newInstance();  
  85.         // 从消息工厂创建SOAP消息对象  
  86.         SOAPMessage message = mFactory.createMessage();  
  87.         // 创建SOAPPart对象  
  88.         SOAPPart part = message.getSOAPPart();  
  89.         // 创建SOAP信封对象  
  90.         SOAPEnvelope envelope = part.getEnvelope();  
  91.         // 创建SOAPHeader对象  
  92.         SOAPHeader header = message.getSOAPHeader();  
  93.         // 创建SOAPBody对象  
  94.         SOAPBody body = envelope.getBody();  
  95.           
  96.         // 创建XML的根元素  
  97.         SOAPHeaderElement headerElementRoot = header.addHeaderElement(new QName("http://server./""c""ns1"));  
  98.         SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server./""selectCustomerByName""ns1"));  
  99.         headerElementRoot.addChildElement(new QName("name")).addTextNode("why");  
  100.           
  101.         // 访问Web服务地址  
  102.         SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService"));  
  103.         // 控制台输出返回的SOAP消息  
  104.         OutputStream os = System.out;  
  105.         reMessage.writeTo(os);  
  106.           
  107.         // 输出SOAP消息中的附件  
  108.         Iterator<AttachmentPart> it = reMessage.getAttachments();  
  109.         while (it.hasNext()) {  
  110.             InputStream ins = it.next().getDataHandler().getInputStream();  
  111.             byte[] b = new byte[ins.available()];  
  112.             OutputStream ous = new FileOutputStream("c:\\aaa.jpg");  
  113.             while (ins.read(b) != -1) {  
  114.                 ous.write(b);  
  115.             }  
  116.             ous.close();  
  117.         }  
  118.         connection.close();  
  119.     }  
  120.       
  121.     /** 
  122.      * 调用一个在soap:Body中传递参数的函数 
  123.      * @throws Exception 
  124.      */  
  125.     public static void selectMaxAgeCustomer() throws Exception{  
  126.         // 获取SOAP连接工厂  
  127.         SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();  
  128.         // 从SOAP连接工厂创建SOAP连接对象  
  129.         SOAPConnection connection = factory.createConnection();  
  130.         // 获取消息工厂  
  131.         MessageFactory mFactory = MessageFactory.newInstance();  
  132.         // 从消息工厂创建SOAP消息对象  
  133.         SOAPMessage message = mFactory.createMessage();  
  134.         // 创建SOAPPart对象  
  135.         SOAPPart part = message.getSOAPPart();  
  136.         // 创建SOAP信封对象  
  137.         SOAPEnvelope envelope = part.getEnvelope();  
  138.         // 创建SOAPHeader对象  
  139.         SOAPHeader header = message.getSOAPHeader();  
  140.         // 创建SOAPBody对象  
  141.         SOAPBody body = envelope.getBody();  
  142.   
  143.         // 设置Content-Type  
  144.         MimeHeaders hd = message.getMimeHeaders();   
  145.         hd.setHeader("Content-Type""application/xop+xml; charset=utf-8; type=\"text/xml\"");  
  146.   
  147.         // 创建XML的根元素  
  148.         SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server./""selectMaxAgeCustomer""ns1"));  
  149.           
  150.         // 创建Customer实例1  
  151.         SOAPElement elementC1 = bodyElementRoot.addChildElement(new QName("arg0"));  
  152.         elementC1.addChildElement(new QName("id")).addTextNode("1");  
  153.         elementC1.addChildElement(new QName("name")).addTextNode("A");  
  154.         elementC1.addChildElement(new QName("birthday")).addTextNode("1989-01-28T00:00:00.000+08:00");  
  155.         // 创建附件对象  
  156.         AttachmentPart attachment = message.createAttachmentPart(new DataHandler(new FileDataSource("c:\\c1.jpg")));  
  157.         // 设置Content-ID  
  158.         attachment.setContentId("<" + UUID.randomUUID().toString() + ">");  
  159.         attachment.setMimeHeader("Content-Transfer-Encoding""binary");  
  160.         message.addAttachmentPart(attachment);  
  161.         SOAPElement elementData = elementC1.addChildElement(new QName("imageData"));  
  162.           
  163.         // 添加XOP支持  
  164.         elementData.addChildElement(  
  165.                 new QName("http://www./2004/08/xop/include""Include","xop"))  
  166.                 .addAttribute(new QName("href"),"cid:" + attachment.getContentId().replaceAll("<""").replaceAll(">"""));  
  167.           
  168.         // 创建Customer实例2  
  169.         SOAPElement elementC2 = bodyElementRoot.addChildElement(new QName("arg1"));  
  170.         elementC2.addChildElement(new QName("id")).addTextNode("2");  
  171.         elementC2.addChildElement(new QName("name")).addTextNode("B");  
  172.         elementC2.addChildElement(new QName("birthday")).addTextNode("1990-01-28T00:00:00.000+08:00");  
  173.         AttachmentPart attachment2 = message.createAttachmentPart(new DataHandler(new FileDataSource("c:\\c2.jpg")));  
  174.         attachment2.setContentId("<" + UUID.randomUUID().toString() + ">");  
  175.         message.addAttachmentPart(attachment2);  
  176.         SOAPElement elementData2 = elementC2.addChildElement(new QName("imageData"));  
  177.           
  178.         elementData2.addChildElement(  
  179.                 new QName("http://www./2004/08/xop/include""Include","xop"))  
  180.                 .addAttribute(new QName("href"),"cid:" + attachment2.getContentId().replaceAll("<""").replaceAll(">"""));  
  181.           
  182.         // 控制台输出发送的SOAP消息  
  183.         OutputStream os = new ByteArrayOutputStream();  
  184.         message.writeTo(os);  
  185.         String soapStr = os.toString();  
  186.         System.out.println("\n@@@@@@@@@@@@@@@@@@\n"+soapStr+"\n@@@@@@@@@@@@@@@@@@");  
  187.           
  188.         // 访问Web服务地址  
  189.         SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService"));  
  190.         // 控制台输出返回的SOAP消息  
  191.         OutputStream baos = new ByteArrayOutputStream();  
  192.         reMessage.writeTo(baos);  
  193.         String soapStr2 = baos.toString();  
  194.         System.out.println("\n#############\n"+soapStr2+"\n################");  
  195.           
  196. //      // 输出SOAP消息中的第一个子元素的元素名称  
  197.         System.out.println("\n<<<<<<<<<<<<<<<<<<<" + reMessage.getSOAPBody().getFirstChild().getLocalName());  
  198.         // 输出SOAP消息中的附件  
  199.         Iterator<AttachmentPart> it = reMessage.getAttachments();  
  200.         while (it.hasNext()) {  
  201.             InputStream ins = it.next().getDataHandler().getInputStream();  
  202.             byte[] b = new byte[ins.available()];  
  203.             OutputStream ous = new FileOutputStream("c:\\bbb.jpg");  
  204.             while (ins.read(b) != -1) {  
  205.                 ous.write(b);  
  206.             }  
  207.             ous.close();  
  208.         }  
  209.           
  210.         connection.close();  
  211.           
  212.     }  
  213. }  

 

    使用SAAJ创建附件时,需设置Content-Type=application/xop+xml; charset=utf-8; type="text/xml",否则服务器端获取不到这个附件,查看发送给服务器端的SOAP消息可以看到,默认Content-Type被置为text/xml; charset=utf-8,因此,需在代码中加入:

Java代码  收藏代码
  1. MimeHeaders hd = message.getMimeHeaders();   
  2. hd.setHeader("Content-Type""application/xop+xml; charset=utf-8; type=\"text/xml\"");  
 

    SOAPMessage有一个writeTo(OutputStream os)方法,可以将整个SOAP消息的内容写入一个输出流中,我们可以截获这个输出流的内容进行分析或再次整理。

 

附件是我的工程(2010-11-15更新)


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多