JAVA中三种WebService规范及底层实例

    http://www.360doc.com/content/11/1104/16/1073512_161674938.shtml
这个系列文章写得真的很不错,由浅入深为表尊重请直接访问原文。
JAVA6开发WebService (一)
http://wuhongyu.iteye.com/blog/807470
JAVA6开发WebService (二)——JAX-WS例子
http://wuhongyu.iteye.com/blog/807836
JAVA6开发WebService (三)——几个概念
http://wuhongyu.iteye.com/blog/808922
JAVA6开发WebService (四)——SAAJ调用WebService
http://wuhongyu.iteye.com/blog/810571
下面的复制内容仅作以后可能的找不到和知识管理的用途。

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代码  收藏代码

    package com.why.webservice; 
     
    import javax.jws.WebService; 
    import javax.xml.ws.Endpoint; 
     
    /**
     * 
     * @author why
     *
     */ 
    @WebService 
    public class Hello { 
     
        public String sayHello(String name) { 
            return "Hello " + name; 
        } 
     
        public static void main(String[] args){ 
            Endpoint.publish("http://localhost:8080/com.why.webservice.Hello", new Hello()); 
            System.out.println("Success"); 
        } 
    } 



客户端:

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


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


Java代码  收藏代码

    package com.why.client; 
     
    /**
     * 
     * @author why
     *
     */ 
    public class HelloClient { 
     
        /**
         * @param args
         */ 
        public static void main(String[] args) { 
            Hello hello = new HelloService().getHelloPort(); 
            String s = hello.sayHello("why"); 
            System.out.println(s); 
        } 
    } 



执行代码,输出:Hello why

2。

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



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



先来看Customer类:


Java代码  收藏代码

    package com.why.server; 
     
    import java.util.Date; 
     
    import javax.activation.DataHandler; 
    import javax.xml.bind.annotation.XmlAccessType; 
    import javax.xml.bind.annotation.XmlAccessorType; 
    import javax.xml.bind.annotation.XmlMimeType; 
    import javax.xml.bind.annotation.XmlRootElement; 
     
    @XmlRootElement(name = "Customer") 
    @XmlAccessorType(XmlAccessType.FIELD) 
    public class Customer { 
        private long id; 
        private String name; 
        private Date birthday; 
        @XmlMimeType("application/octet-stream") 
        private DataHandler imageData; 
         
            //getter and setter 
            ...... 
    } 

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

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



接口类:
Java代码  收藏代码

    package com.why.server; 
     
    import javax.jws.WebParam; 
    import javax.jws.WebService; 
    import javax.xml.ws.soap.MTOM; 
     
    /**
     * 
     * @author why
     *
     */ 
    @WebService(name="Hello") 
    @SOAPBinding(style = SOAPBinding.Style.RPC) 
    @MTOM 
    public interface Hello { 
        public void printContext(); 
        public Customer selectCustomerByName(@WebParam(name = "customer")Customer customer); 
        public Customer selectMaxAgeCustomer(Customer c1, Customer c2); 
    } 

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

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

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



实现类:
Java代码  收藏代码

    package com.why.server; 
     
    import java.io.File; 
    import java.io.FileOutputStream; 
    import java.io.IOException; 
    import java.io.InputStream; 
    import java.io.OutputStream; 
    import java.text.ParseException; 
    import java.text.SimpleDateFormat; 
    import java.util.Date; 
    import java.util.Set; 
    import javax.activation.DataHandler; 
    import javax.activation.FileDataSource; 
    import javax.annotation.Resource; 
    import javax.jws.WebService; 
    import javax.xml.ws.WebServiceContext; 
    import javax.xml.ws.handler.MessageContext; 
     
    /**
     * 
     * @author why
     *
     */ 
    @WebService(serviceName="HelloService",portName="HelloServicePort",targetNamespace="http://service.why.com/",endpointInterface="com.why.server.Hello") 
    public class HelloImpl implements Hello { 
         
        @Resource 
        private WebServiceContext context; 
         
        @Override 
        public void printContext(){ 
            MessageContext ctx = context.getMessageContext(); 
            Set<String> set = ctx.keySet(); 
            for (String key : set) { 
                System.out.println("{" + key + "," + ctx.get(key) +"}"); 
                try { 
                    System.out.println("key.scope=" + ctx.getScope(key)); 
                } catch (Exception e) { 
                    System.out.println(key + " is not exits"); 
                } 
            } 
        } 
         
        @Override 
        public Customer selectCustomerByName(Customer customer) { 
            if("why".equals(customer.getName())){ 
                customer.setId(1); 
                try { 
                    customer.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07")); 
                } catch (ParseException e) { 
                    e.printStackTrace(); 
                } 
                customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "why.jpg")))); 
            }else{ 
                customer.setId(2); 
                customer.setBirthday(new Date()); 
                customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "origin.jpg")))); 
            } 
            return customer; 
        } 
         
        @Override 
        public Customer selectMaxAgeCustomer(Customer c1, Customer c2) { 
            try { 
                // 输出接收到的附件 
                System.out.println("c1.getImageData().getContentType()=" + c1.getImageData().getContentType()); 
                InputStream is = c2.getImageData().getInputStream(); 
                OutputStream os = new FileOutputStream("c:\\temp1.jpg"); 
                byte[] bytes = new byte[1024]; 
                int c; 
                while ((c = is.read(bytes)) != -1) { 
                    os.write(bytes, 0, c); 
                } 
                os.close(); 
                 
                System.out.println("c2.getImageData().getContentType()=" + c2.getImageData().getContentType()); 
                is = c2.getImageData().getInputStream(); 
                os = new FileOutputStream("c:\\temp2.jpg"); 
                bytes = new byte[1024]; 
                while ((c = is.read(bytes)) != -1) { 
                    os.write(bytes, 0, c); 
                } 
                os.close(); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
             
            if (c1.getBirthday().getTime() > c2.getBirthday().getTime()){ 
                return c2; 
            } 
            else{ 
                return c1; 
            } 
        } 
    } 



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

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



发布这个服务:
Java代码  收藏代码

    package com.why.server; 
     
    import javax.xml.ws.Endpoint; 
     
    /**
     * 
     * @author why
     *
     */ 
    public class SoapServer { 
        public static void main(String[] args) { 
            Endpoint.publish("http://localhost:8080/helloService",new HelloImpl()); 
        } 
    } 



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

    package com.why.client; 
    import java.io.FileOutputStream; 
    import java.io.IOException; 
    import java.io.InputStream; 
    import java.io.OutputStream; 
    import java.net.MalformedURLException; 
    import java.net.URL; 
    import java.text.ParseException; 
    import java.text.SimpleDateFormat; 
    import java.util.GregorianCalendar; 
    import javax.activation.DataHandler; 
    import javax.activation.DataSource; 
    import javax.activation.FileDataSource; 
    import javax.xml.datatype.DatatypeConfigurationException; 
    import javax.xml.datatype.DatatypeFactory; 
    import javax.xml.namespace.QName; 
     
    /**
     * 
     * @author why
     *
     */ 
    public class SoapClient { 
        public static void main(String[] args) throws ParseException, MalformedURLException { 
            QName qName = new QName("http://service.why.com/","HelloService"); 
            HelloService helloService = new HelloService(new URL("http://127.0.0.1:8080/helloService?wsdl"),qName); 
            Hello hello = (Hello) helloService.getPort(Hello.class); 
             
            hello.printContext(); 
             
            System.out.println("---------------------------------------------------"); 
             
            Customer customer = new Customer(); 
            customer.setName("why"); 
            DataSource ds = hello.selectCustomerByName(customer).getImageData().getDataSource(); 
            String attachmentMimeType = ds.getContentType(); 
            System.out.println(attachmentMimeType); 
            try { 
                InputStream is = ds.getInputStream(); 
                OutputStream os = new FileOutputStream("c:\\why_temp.jpg"); 
                byte[] bytes = new byte[1024]; 
                int c; 
                while ((c = is.read(bytes)) != -1) { 
                    os.write(bytes, 0, c); 
                } 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
             
            System.out.println("########################################"); 
             
            Customer c1 = new Customer(); 
            c1.setId(1); 
            c1.setName("why"); 
            GregorianCalendar calendar = (GregorianCalendar)GregorianCalendar.getInstance(); 
            calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07")); 
            try { 
                c1.setBirthday(DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar)); 
            } catch (DatatypeConfigurationException e) { 
                e.printStackTrace(); 
            } 
            c1.setImageData(new DataHandler(new FileDataSource("c:\\c1.jpg"))); 
             
            Customer c2 = new Customer(); 
            c2.setId(2); 
            c2.setName("abc"); 
            calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1986-10-07")); 
            try { 
                c2.setBirthday(DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar)); 
            } catch (DatatypeConfigurationException e) { 
                e.printStackTrace(); 
            } 
            c2.setImageData(new DataHandler(new FileDataSource("c:\\c2.jpg"))); 
             
            Customer c = hello.selectMaxAgeCustomer(c1,c2); 
            System.out.println(c.getName()); 
             
        } 
    } 



附件是我的工程,当然运行这个程序,需先在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代码  收藏代码

    <?xml version="1.0" encoding="UTF-8"?> 
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 
        <soap:Header> 
            ... 
        </soap:Header> 
        <soap:Body> 
            ... 
            <soap:Fault> 
                ... 
            </soap:Fault> 
        </soap:Body> 
    </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.iteye.com/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代码  收藏代码

    <definitions> 
        <types> 
            ... 
        </types> 
        <message> 
            ... 
        </message> 
        <portType> 
            ... 
        </portType> 
        <binding> 
            ... 
        </binding> 
            <service> 
            ... 
        </service> 
    </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代码  收藏代码

        public static void main(String[] args) { 
    //      Endpoint.publish("http://localhost:8080/helloService",new HelloImpl()); 
             
            //使用CXF特有的API---JaxWsServerFactoryBean发布 
            JaxWsServerFactoryBean soapFactoryBean = new JaxWsServerFactoryBean(); 
            soapFactoryBean.getInInterceptors().add(new LoggingInInterceptor()); 
            soapFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor()); 
            soapFactoryBean.setServiceClass(HelloImpl.class); 
            soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService"); 
            soapFactoryBean.create(); 
        } 
    } 



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

            //1、使用标准的JAX-WS 的API 完成客户端调用 
    //      QName qName = new QName("http://service.why.com/","HelloService"); 
    //      HelloService helloService = new HelloService(new URL("http://127.0.0.1:8080/helloService?wsdl"),qName); 
    //      Hello hello = (Hello) helloService.getPort(Hello.class); 
             
            //2、使用了CXF 的JaxWsProxyFactoryBean 来访问Web 服务     
            JaxWsProxyFactoryBean soapFactoryBean = new JaxWsProxyFactoryBean(); 
            soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService"); 
            soapFactoryBean.setServiceClass(Hello.class); 
            Object o = soapFactoryBean.create(); 
            Hello hello = (Hello) o; 



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



Apache CXF下载地址 :http://cxf.apache.org/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代码  收藏代码

    package com.why.server; 
     
    import javax.jws.WebParam; 
    import javax.jws.WebService; 
    import javax.jws.soap.SOAPBinding; 
    import javax.xml.ws.soap.MTOM; 
     
    /**
     * 
     * @author why
     *
     */ 
    @WebService(name="Hello") 
    @SOAPBinding(style = SOAPBinding.Style.RPC) 
    public interface Hello { 
        public void printContext(); 
        public Customer selectCustomerByName(@WebParam(name = "c",header=true)Customer customer); 
        public Customer selectMaxAgeCustomer(Customer c1, Customer c2); 
    } 

实现类:
Java代码  收藏代码

    package com.why.server; 
     
    import java.io.File; 
    import java.io.FileOutputStream; 
    import java.io.IOException; 
    import java.io.InputStream; 
    import java.io.OutputStream; 
    import java.text.ParseException; 
    import java.text.SimpleDateFormat; 
    import java.util.Date; 
    import java.util.Set; 
    import javax.activation.DataHandler; 
    import javax.activation.FileDataSource; 
    import javax.annotation.Resource; 
    import javax.jws.WebService; 
    import javax.xml.ws.WebServiceContext; 
    import javax.xml.ws.handler.MessageContext; 
    import javax.xml.ws.soap.MTOM; 
     
    /**
     * 
     * 通过@MTOM注解启动MTOM传输方式,使用CXF实现时,这个注解放在接口或者实现类上都可以,使用JDK1.6自带实现时,需标注在实现类上
     * @author why
     *
     */ 
    @WebService(serviceName="HelloService",portName="HelloServicePort",targetNamespace="http://service.why.com/",endpointInterface="com.why.server.Hello") 
    @MTOM 
    public class HelloImpl implements Hello { 
         
        @Resource 
        private WebServiceContext context; 
         
        @Override 
        public void printContext(){ 
            MessageContext ctx = context.getMessageContext(); 
            Set<String> set = ctx.keySet(); 
            for (String key : set) { 
                System.out.println("{" + key + "," + ctx.get(key) +"}"); 
                try { 
                    System.out.println("key.scope=" + ctx.getScope(key)); 
                } catch (Exception e) { 
                    System.out.println(key + " is not exits"); 
                } 
            } 
        } 
         
        @Override 
        public Customer selectCustomerByName(Customer customer) { 
            if("why".equals(customer.getName())){ 
                customer.setId(1); 
                try { 
                    customer.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07")); 
                } catch (ParseException e) { 
                    e.printStackTrace(); 
                } 
                customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "why.jpg")))); 
            }else{ 
                customer.setId(2); 
                customer.setBirthday(new Date()); 
                customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "origin.jpg")))); 
            } 
            return customer; 
        } 
         
        @Override 
        public Customer selectMaxAgeCustomer(Customer c1, Customer c2) { 
            try { 
                // 输出接收到的附件 
                System.out.println("c1.getImageData().getContentType()=" + c1.getImageData().getContentType()); 
                InputStream is = c1.getImageData().getInputStream(); 
                OutputStream os = new FileOutputStream("c:\\temp1.jpg"); 
                byte[] bytes = new byte[1024]; 
                int c; 
                while ((c = is.read(bytes)) != -1) { 
                    os.write(bytes, 0, c); 
                } 
                os.close(); 
                 
                System.out.println("c2.getImageData().getContentType()=" + c2.getImageData().getContentType()); 
                is = c2.getImageData().getInputStream(); 
                os = new FileOutputStream("c:\\temp2.jpg"); 
                bytes = new byte[1024]; 
                while ((c = is.read(bytes)) != -1) { 
                    os.write(bytes, 0, c); 
                } 
                os.close(); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
             
            if (c1.getBirthday().getTime() > c2.getBirthday().getTime()){ 
                return c2; 
            } 
            else{ 
                return c1; 
            } 
        } 
    } 

Customer类:
Java代码  收藏代码

    package com.why.server; 
     
    import java.util.Date; 
     
    import javax.activation.DataHandler; 
    import javax.xml.bind.annotation.XmlAccessType; 
    import javax.xml.bind.annotation.XmlAccessorType; 
    import javax.xml.bind.annotation.XmlMimeType; 
    import javax.xml.bind.annotation.XmlRootElement; 
     
    /**
     * 
     * @author why
     *
     */ 
    @XmlRootElement(name = "Customer") 
    @XmlAccessorType(XmlAccessType.FIELD) 
    public class Customer { 
        private long id; 
        private String name; 
        private Date birthday; 
        @XmlMimeType("application/octet-stream") 
        private DataHandler imageData; 
         
        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; 
        } 
        public DataHandler getImageData() { 
            return imageData; 
        } 
        public void setImageData(DataHandler imageData) { 
            this.imageData = imageData; 
        } 
    } 

发布:
Java代码  收藏代码

    package com.why.server; 
     
    import javax.xml.ws.Endpoint; 
     
    /**
     * 
     * @author why
     *
     */ 
    public class SoapServer { 
        public static void main(String[] args) { 
            Endpoint.publish("http://localhost:8080/helloService",new HelloImpl()); 
     
        } 
    } 



    这次不生成客户端类,而是通过自己组织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代码  收藏代码

    // 获取SOAP连接工厂 
    SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance(); 
    // 从SOAP连接工厂创建SOAP连接对象 
    SOAPConnection connection = factory.createConnection(); 
    // 获取消息工厂 
    MessageFactory mFactory = MessageFactory.newInstance(); 
    // 从消息工厂创建SOAP消息对象 
    SOAPMessage message = mFactory.createMessage(); 
    // 创建SOAPPart对象 
    SOAPPart part = message.getSOAPPart(); 
    // 创建SOAP信封对象 
    SOAPEnvelope envelope = part.getEnvelope(); 
    // 创建SOAPHeader对象 
    SOAPHeader header = message.getSOAPHeader(); 
    // 创建SOAPBody对 
    SOAPBody body = envelope.getBody(); 



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


Java代码  收藏代码

    package com.why.client; 
    import java.io.ByteArrayOutputStream; 
    import java.io.FileOutputStream; 
    import java.io.InputStream; 
    import java.io.OutputStream; 
    import java.net.URL; 
    import java.util.Iterator; 
    import java.util.UUID; 
    import javax.activation.DataHandler; 
    import javax.activation.FileDataSource; 
    import javax.xml.namespace.QName; 
    import javax.xml.soap.AttachmentPart; 
    import javax.xml.soap.MessageFactory; 
    import javax.xml.soap.SOAPBody; 
    import javax.xml.soap.SOAPBodyElement; 
    import javax.xml.soap.SOAPConnection; 
    import javax.xml.soap.SOAPConnectionFactory; 
    import javax.xml.soap.SOAPElement; 
    import javax.xml.soap.SOAPEnvelope; 
    import javax.xml.soap.SOAPHeader; 
    import javax.xml.soap.SOAPHeaderElement; 
    import javax.xml.soap.SOAPMessage; 
    import javax.xml.soap.SOAPPart; 
     
    /**
     * 
     * @author why
     *
     */ 
    public class SoapClient { 
        public static void main(String[] args) throws Exception{ 
             
            printContext(); 
             
            selectCustomerByName(); 
             
            selectMaxAgeCustomer(); 
        } 
         
        /**
         * 调用一个无参函数
         * @throws Exception
         */ 
        public static void printContext() throws Exception{ 
            // 获取SOAP连接工厂 
            SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance(); 
            // 从SOAP连接工厂创建SOAP连接对象 
            SOAPConnection connection = factory.createConnection(); 
            // 获取消息工厂 
            MessageFactory mFactory = MessageFactory.newInstance(); 
            // 从消息工厂创建SOAP消息对象 
            SOAPMessage message = mFactory.createMessage(); 
            // 创建SOAPPart对象 
            SOAPPart part = message.getSOAPPart(); 
            // 创建SOAP信封对象 
            SOAPEnvelope envelope = part.getEnvelope(); 
            // 创建SOAPHeader对象 
            SOAPHeader header = message.getSOAPHeader(); 
            // 创建SOAPBody对象 
            SOAPBody body = envelope.getBody(); 
             
            // 创建XML的根元素 
            SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server.why.com/", "printContext", "ns1")); 
             
            // 访问Web服务地址 
            SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService")); 
            // 控制台输出返回的SOAP消息 
            OutputStream os = System.out; 
            reMessage.writeTo(os); 
             
            connection.close(); 
        } 
         
        /**
         * 调用一个在soap:HEADER中传递参数的函数
         * @throws Exception
         */ 
        public static void selectCustomerByName() throws Exception{ 
            // 获取SOAP连接工厂 
            SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance(); 
            // 从SOAP连接工厂创建SOAP连接对象 
            SOAPConnection connection = factory.createConnection(); 
            // 获取消息工厂 
            MessageFactory mFactory = MessageFactory.newInstance(); 
            // 从消息工厂创建SOAP消息对象 
            SOAPMessage message = mFactory.createMessage(); 
            // 创建SOAPPart对象 
            SOAPPart part = message.getSOAPPart(); 
            // 创建SOAP信封对象 
            SOAPEnvelope envelope = part.getEnvelope(); 
            // 创建SOAPHeader对象 
            SOAPHeader header = message.getSOAPHeader(); 
            // 创建SOAPBody对象 
            SOAPBody body = envelope.getBody(); 
             
            // 创建XML的根元素 
            SOAPHeaderElement headerElementRoot = header.addHeaderElement(new QName("http://server.why.com/", "c", "ns1")); 
            SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server.why.com/", "selectCustomerByName", "ns1")); 
            headerElementRoot.addChildElement(new QName("name")).addTextNode("why"); 
             
            // 访问Web服务地址 
            SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService")); 
            // 控制台输出返回的SOAP消息 
            OutputStream os = System.out; 
            reMessage.writeTo(os); 
             
            // 输出SOAP消息中的附件 
            Iterator<AttachmentPart> it = reMessage.getAttachments(); 
            while (it.hasNext()) { 
                InputStream ins = it.next().getDataHandler().getInputStream(); 
                byte[] b = new byte[ins.available()]; 
                OutputStream ous = new FileOutputStream("c:\\aaa.jpg"); 
                while (ins.read(b) != -1) { 
                    ous.write(b); 
                } 
                ous.close(); 
            } 
            connection.close(); 
        } 
         
        /**
         * 调用一个在soap:Body中传递参数的函数
         * @throws Exception
         */ 
        public static void selectMaxAgeCustomer() throws Exception{ 
            // 获取SOAP连接工厂 
            SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance(); 
            // 从SOAP连接工厂创建SOAP连接对象 
            SOAPConnection connection = factory.createConnection(); 
            // 获取消息工厂 
            MessageFactory mFactory = MessageFactory.newInstance(); 
            // 从消息工厂创建SOAP消息对象 
            SOAPMessage message = mFactory.createMessage(); 
            // 创建SOAPPart对象 
            SOAPPart part = message.getSOAPPart(); 
            // 创建SOAP信封对象 
            SOAPEnvelope envelope = part.getEnvelope(); 
            // 创建SOAPHeader对象 
            SOAPHeader header = message.getSOAPHeader(); 
            // 创建SOAPBody对象 
            SOAPBody body = envelope.getBody(); 
     
            // 设置Content-Type 
            MimeHeaders hd = message.getMimeHeaders();  
            hd.setHeader("Content-Type", "application/xop+xml; charset=utf-8; type=\"text/xml\""); 
     
            // 创建XML的根元素 
            SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server.why.com/", "selectMaxAgeCustomer", "ns1")); 
             
            // 创建Customer实例1 
            SOAPElement elementC1 = bodyElementRoot.addChildElement(new QName("arg0")); 
            elementC1.addChildElement(new QName("id")).addTextNode("1"); 
            elementC1.addChildElement(new QName("name")).addTextNode("A"); 
            elementC1.addChildElement(new QName("birthday")).addTextNode("1989-01-28T00:00:00.000+08:00"); 
            // 创建附件对象 
            AttachmentPart attachment = message.createAttachmentPart(new DataHandler(new FileDataSource("c:\\c1.jpg"))); 
            // 设置Content-ID 
            attachment.setContentId("<" + UUID.randomUUID().toString() + ">"); 
            attachment.setMimeHeader("Content-Transfer-Encoding", "binary"); 
            message.addAttachmentPart(attachment); 
            SOAPElement elementData = elementC1.addChildElement(new QName("imageData")); 
             
            // 添加XOP支持 
            elementData.addChildElement( 
                    new QName("http://www.w3.org/2004/08/xop/include", "Include","xop")) 
                    .addAttribute(new QName("href"),"cid:" + attachment.getContentId().replaceAll("<", "").replaceAll(">", "")); 
             
            // 创建Customer实例2 
            SOAPElement elementC2 = bodyElementRoot.addChildElement(new QName("arg1")); 
            elementC2.addChildElement(new QName("id")).addTextNode("2"); 
            elementC2.addChildElement(new QName("name")).addTextNode("B"); 
            elementC2.addChildElement(new QName("birthday")).addTextNode("1990-01-28T00:00:00.000+08:00"); 
            AttachmentPart attachment2 = message.createAttachmentPart(new DataHandler(new FileDataSource("c:\\c2.jpg"))); 
            attachment2.setContentId("<" + UUID.randomUUID().toString() + ">"); 
            message.addAttachmentPart(attachment2); 
            SOAPElement elementData2 = elementC2.addChildElement(new QName("imageData")); 
             
            elementData2.addChildElement( 
                    new QName("http://www.w3.org/2004/08/xop/include", "Include","xop")) 
                    .addAttribute(new QName("href"),"cid:" + attachment2.getContentId().replaceAll("<", "").replaceAll(">", "")); 
             
            // 控制台输出发送的SOAP消息 
            OutputStream os = new ByteArrayOutputStream(); 
            message.writeTo(os); 
            String soapStr = os.toString(); 
            System.out.println("\n@@@@@@@@@@@@@@@@@@\n"+soapStr+"\n@@@@@@@@@@@@@@@@@@"); 
             
            // 访问Web服务地址 
            SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService")); 
            // 控制台输出返回的SOAP消息 
            OutputStream baos = new ByteArrayOutputStream(); 
            reMessage.writeTo(baos); 
            String soapStr2 = baos.toString(); 
            System.out.println("\n#############\n"+soapStr2+"\n################"); 
             
    //      // 输出SOAP消息中的第一个子元素的元素名称 
            System.out.println("\n<<<<<<<<<<<<<<<<<<<" + reMessage.getSOAPBody().getFirstChild().getLocalName()); 
            // 输出SOAP消息中的附件 
            Iterator<AttachmentPart> it = reMessage.getAttachments(); 
            while (it.hasNext()) { 
                InputStream ins = it.next().getDataHandler().getInputStream(); 
                byte[] b = new byte[ins.available()]; 
                OutputStream ous = new FileOutputStream("c:\\bbb.jpg"); 
                while (ins.read(b) != -1) { 
                    ous.write(b); 
                } 
                ous.close(); 
            } 
             
            connection.close(); 
             
        } 
    } 



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

    MimeHeaders hd = message.getMimeHeaders();  
    hd.setHeader("Content-Type", "application/xop+xml; charset=utf-8; type=\"text/xml\""); 



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



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

你可能感兴趣的:(webservice)