Web Service

来源:互联网 发布:手机淘宝怎么寄件 编辑:程序博客网 时间:2024/06/08 02:49

Web Service

 

Web Service是一个平台独立的、松耦合的,主要应用于异构系统间通信和分布式应用。在Java中,实现分布式应用一般有两种方式:一是,基于消息方式实现系统间的通信;二是, 基于远程调用方式实现系统间通信。

在Java中提供了对webservice的支持,jax-ws规范是一组xmlweb service的Javaapi,jax-ws允许开发者可以选择基于RPC或Message来实现自己的webservice。

在Java中编写webservice应用程序,需要依据jax-ws规范来编程或采用第三方框架如CXF等。在服务器端,通过Java语言定义服务接口,并提供相关实现;之后通过jax-ws的服务发布接口就可以将其发布为webservice。在客户端,用户可以通过jax-ws的api创建一个代理来实现webservice的访问。

1、       服务

1.1、接口

在实现webservice时,首先要定义服务接口。在Java中直接定义一个接口,之后在接口上添加@WebService注解,即可声明一个webservice接口,如下

@WebService

public interface CalculationService

{

   public int add(int a,int b);

   public int divide(inta,intb);

}

1.2、实现

在Java中实现,webservice接口,只要添加实现类,并添加相关注解,下面的类实现了CalculationService接口,如下:

@WebService(endpointInterface="ws.CalculationService",

            serviceName="CalculationService",

            portName="CalculationPort",

            targetNamespace="http://www.ssl.org")

public class CalculationServiceImpl implementsCalculationService

{

 

     @Override

     public int add(int a, int b)

     {

          returna+b;

     }

 

     @Override

     public int divide(inta,intb)

     {

          returna/b;

     }

}

1.3、发布

在JavaSE中发布web服务相当简单,只要指定服务地址和服务实现类即可,如下:

public class Server

{

   public static void main(String[] args)

  {

      Endpoint.publish("http://127.0.0.1:8080/cal",new CalculationServiceImpl());

  }

}

在浏览器中输入http://127.0.0.1:8080/cal?wsdl即可查看CalculationService服务的wsdl文档。

1.4、客户端

使用CalculationService服务最简单的方式,就是通过jdk自带的工具wsimport,生成客户端代理类,如下

wsimport-keep -d ~/client http://127.0.0.1:8080/cal?wsdl

在终端输入上面命令,即可生成客户端代理类,以便在客户端方便访问CalculationService的服务,

public class Client

{

      public static void main(String[] args)

      {

          CalculationService port=newCalculationService_Service().getCalculationPort();

          System.out.println(port.add(1, 2));

      }

}

1.5、总结

1.1-1.4章节完整实现了一个webservice的例子,其中涉及了一些重要的概念,如wsdl等。

2、       原理

Web Service标准主要wsdl、soap等构成。wsdl为web服务描述语言,以xml格式详细描述了服务的各个部分,如数据类型、消息、接口、协议以及服务地址等。soap为简单对象访问协议,采用XML格式传输数据;web服务的调用过程本质上就是soap消息的发送和接受过程,如下


1)     客户端将需要的调用信息方法名、参数等封装为XML片段(SOAP消息),并传输给服务器;

2)     服务器接收soap消息(xml文档),解析、转换后执行相应的方法,并把执行结果封装为soap消息,返回给客户端;

3)     客户端接收soap消息,进行解析、转换,得到结果。

3、       soap

通过第二章,我们知道web服务的调用过程本质上就是soap消息的发送和接收过程。在1.4节,我们采用wsimport工具生成了代理来访问web服务,其实现方式也是对soap消息的封装。jax-ws提供了相关api来直接操作soap消息,如下

String ns="http://www.ssl.org";

 //1. 创建服务

 URL ur=new URL("http://127.0.0.1:8080/cal?wsdl");

 QName qname=new QName(ns,"CalculationService");

 Service service=Service.create(ur,qname);

                

 //2.创建消息

SOAPMessage message=MessageFactory.newInstance().createMessage();

SOAPBody body=message.getSOAPPart().getEnvelope().getBody();

SOAPBodyElement element=body.addBodyElement(new QName("http://ws/","add","ns"));

element.addChildElement("arg0").setValue("1");

element.addChildElement("arg1").setValue("2");

                

 message.writeTo(System.out);

 System.out.println("\ninvoke.....");

 //3 发送消息

Dispatch<SOAPMessage> dispatch=service.createDispatch(new QName(ns,"CalculationPort"), SOAPMessage.class,Service.Mode.MESSAGE);

 SOAPMessage response=dispatch.invoke(message);

  response.writeTo(System.out);

上面代码发送和接受的消息如下:

// 发送的消息

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

<SOAP-ENV:Header/>

<SOAP-ENV:Body>

<ns:addxmlns:ns="http://ws/">

<arg0>1</arg0>

<arg1>2</arg1>

</ns:add>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

// 响应的消息

<S:Envelopexmlns:S="http://schemas.xmlsoap.org/soap/envelope/"xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

<SOAP-ENV:Header/>

<S:Body>

<ns2:addResponsexmlns:ns2="http://ws/">

<return>3</return>

</ns2:addResponse>

</S:Body>

</S:Envelope>

注意:

l   显示使用消息通信时,需要根据wsdl文档,准确指明服务的名称、端口的名称以及服务的命名空间;

l   body中传输消息的数据时,需要指定数据类型的命名空间;注意这些数据类型可能在单独的schema文件中定义,因此数据类型的命名空间可能和服务的命名空间不一致;

l   soap消息由enveloppartheaderbody等组成;

4、       契约优先

在实现web服务时,一般有两种方式:一种是代码优先;另一种是契约优先。

代码优先,就是第一章web服务的实现方式,定义服务接口、提供实现类、发布服务,其wsdl文档根据服务的接口生成;而契约优先强调契约优于配置,强调先定义wsdl文档,以此文档生成web服务接口,之后提供实现类。下面采用契约优先的方式来实现web服务。

4.1、定义wsdl文档

定义wsdl文档,过程如下:

l  定义数据类型;

l  定义消息;

l  定义接口(port)

l  绑定

l  定义服务

<?xmlversion="1.0"encoding="UTF-8"standalone="no"?>

<wsdl:definitionsxmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

                  xmlns:tns="http://www.ssl.org/cal/"

                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"

                  name="CalculationService"

                 targetNamespace="http://www.ssl.org/cal/">

  <wsdl:types>

    <xsd:schematargetNamespace="http://www.ssl.org/cal/">

        <xsd:elementname="add"type="tns:add"/>

        <xsd:elementname="addResponse"type="tns:addResponse"/>

        <xsd:elementname="divide"type="tns:divide"/>

        <xsd:elementname="divideResponse"type="tns:divideResponse"/>

       

        <xsd:complexTypename="add">

            <xsd:sequence>

               <xsd:elementname="a"type="xsd:int"/>

               <xsd:elementname="b"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

        <xsd:complexTypename="addResponse">

            <xsd:sequence>

                 <xsd:elementname="addResponse"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

       

          <xsd:complexTypename="divide">

            <xsd:sequence>

               <xsd:elementname="a"type="xsd:int"/>

               <xsd:elementname="b"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

        <xsd:complexTypename="divideResponse">

            <xsd:sequence>

                 <xsd:elementname="divideResponse"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

       

    </xsd:schema>

  </wsdl:types>

 

  <wsdl:messagename="add">

    <wsdl:partelement="tns:add"name="parameters"/>

  </wsdl:message>

  <wsdl:messagename="addResponse">

    <wsdl:partelement="tns:addResponse"name="return"/>

  </wsdl:message>

 

  <wsdl:messagename="divide">

    <wsdl:partelement="tns:divide"name="parameters"/>

  </wsdl:message>

  <wsdl:messagename="divideResponse">

    <wsdl:partelement="tns:divideResponse"name="return"/>

  </wsdl:message>

 

  <wsdl:portTypename="cal">

    <wsdl:operationname="add">

      <wsdl:inputmessage="tns:add"/>

      <wsdl:outputmessage="tns:addResponse"/>

    </wsdl:operation>

   

     <wsdl:operationname="divide">

      <wsdl:inputmessage="tns:divide"/>

      <wsdl:outputmessage="tns:divideResponse"/>

    </wsdl:operation>

  </wsdl:portType>

 

  <wsdl:bindingname="calSOAP"type="tns:cal">

    <soap:bindingstyle="document"transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operationname="add">

      <soap:operationsoapAction=""/>

      <wsdl:input>

        <soap:bodyuse="literal"/>

      </wsdl:input>

      <wsdl:output>

        <soap:bodyuse="literal"/>

      </wsdl:output>

    </wsdl:operation>

   

    <wsdl:operationname="divide">

      <soap:operationsoapAction=""/>

      <wsdl:input>

        <soap:bodyuse="literal"/>

      </wsdl:input>

      <wsdl:output>

        <soap:bodyuse="literal"/>

      </wsdl:output>

    </wsdl:operation>

   

  </wsdl:binding>

  <wsdl:servicename="CalculationService">

    <wsdl:portbinding="tns:calSOAP"name="CalculationPort">

      <soap:addresslocation="http://127.0.0.1:8080/cal"/>

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

 

在定义wsdl时,需要注意以下几点:

l   服务的名称以及命名空间;

l   数据类型的命名空间,该命名空间可与服务的命名空间不一致,在显示采用soap消息通信时,需要注意服务和数据类型的命名空间;

l   wsdl:service子元素中,name属性要和wsdl:definitions中的name一致,同时@WebService注解中的serviceName也要和name一致;

l   wsdl:service的子元素wsdl:port中的name属性也和@WebService注解中的portName一致;

4.2、生成接口

wsdl定义后,可以通过wsimport工具来导出接口文件,如下:

@WebService(targetNamespace= "http://www.ssl.org/cal/")

public interface CalService

{

     /**

      *

      * @param b

      * @param a

      * @return returnsint

      */

     @WebMethod

     @WebResult(name = "addResponse", targetNamespace = "")

     @RequestWrapper(localName ="add", targetNamespace ="http://www.ssl.org/cal/")

     @ResponseWrapper(localName ="addResponse", targetNamespace ="http://www.ssl.org/cal/")

     public int add(@WebParam(name = "a", targetNamespace = "") inta,

                      @WebParam(name ="b", targetNamespace ="") intb);

      

     /**

      *

      * @param b

      * @param a

      * @return returnsint

      */

     @WebMethod

     @WebResult(name = "divideResponse", targetNamespace = "")

     @RequestWrapper(localName ="divide", targetNamespace ="http://www.ssl.org/cal/")

     @ResponseWrapper(localName ="divideResponse", targetNamespace ="http://www.ssl.org/cal/")

     public int divide(@WebParam(name = "a", targetNamespace = "") inta,

                         @WebParam(name ="b", targetNamespace ="") intb);

 

}

根据wsdl导出的接口,还要经过修改才能使用,在修改过程中需要注意以下几点:

l   默认情况下,方法的参数和返回值命名空间为空,若要添加命名空间,则消息发送方和消息接收方对应参数或返回值的命名空间必须一致,否则将接收不到参数或返回值;

l   参数或返回值是否有前缀修饰,若返回值有前缀修饰,如下

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

<SOAP-ENV:Header>

<ns:authInfoxmlns:ns="http://www.ssl.org/cal/">ssl

</ns:authInfo>

</SOAP-ENV:Header>

<SOAP-ENV:Body>

<ns:addxmlns:ns="http://www.ssl.org/cal/">

<a>1</a>

<b>2</b>

</ns:add>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

而接收端的数据类型没有前缀,则返回值将不能解析成功。一般定义数据类型会在单独的schema文件中,此时最好设置elementFormDefault="unqualified",该属性将不会在复合数据类型的子元素添加前缀;

l   将所有参数的class属性删除。在服务端,wsimport工具生成的文件中,只会保留接口文件,其他文件并不保留;

4.3、提供实现类

根据wsimport生成的接口文件,提供实现类,如下

@WebService(endpointInterface="org.ssl.cal.CalService",

            targetNamespace="http://www.ssl.org/cal/",

            portName="CalculationPort",

            serviceName="CalculationService",

            wsdlLocation="META-INF/cal.wsdl")

public class CalServiceImpl  implements CalService

{

 

     @Override

     public int add(int a, int b)

     {

          returna+b;

     }

 

     @Override

     public int divide(inta,intb)

     {

          returna/b;

     }

   

}

采用契约优先方式实现web服务,在实现类的@WebService注解中必须添加以下属性:

l  endpointInterface,指明实现的接口,指定实现接口,可以继承接口中的注解;

l  targetNamespace,服务命名空间,与wsdl中的一致;

l  portName与wsdl中的一致;

l  serviceName必须与wsdl中的一致;

l  wsdlLocation:指定wsdl文件的位置;

5、       Fault

soap消息不仅可以传输数据,还可以传输异常信息,异常信息和数据一样都封装在soap消息的body中。采用契约优先方式实现web服务,异常信息定义为一个message字段。现在为CalculationService添加除0异常,步骤如下:

l  定义异常元素

l  添加异常message元素

l  在异常抛出的方法中添加<wsdl:fault>元素

l  在绑定消息时,添加<soap:fault>元素

<?xmlversion="1.0"encoding="UTF-8"standalone="no"?>

<wsdl:definitionsxmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

                  xmlns:tns="http://www.ssl.org/cal/"

                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"

                  name="CalculationService"

                  targetNamespace="http://www.ssl.org/cal/">

  <wsdl:types>

    <xsd:schematargetNamespace="http://www.ssl.org/cal/">

        <xsd:elementname="add"type="tns:add"/>

        <xsd:elementname="addResponse"type="tns:addResponse"/>

        <xsd:elementname="divide"type="tns:divide"/>

        <xsd:elementname="divideResponse"type="tns:divideResponse"/>

        <xsd:elementname="DivideZeroException"type="tns:DivideZeroException"/>

       

        <xsd:complexTypename="add">

            <xsd:sequence>

               <xsd:elementname="a"type="xsd:int"/>

               <xsd:elementname="b"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

        <xsd:complexTypename="addResponse">

            <xsd:sequence>

                 <xsd:elementname="addResponse"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

       

          <xsd:complexTypename="divide">

            <xsd:sequence>

               <xsd:elementname="a"type="xsd:int"/>

               <xsd:elementname="b"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

        <xsd:complexTypename="divideResponse">

            <xsd:sequence>

                 <xsd:elementname="divideResponse"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

       

        <xsd:complexTypename="DivideZeroException">

            <xsd:sequence>

                <xsd:elementname="message"type="xsd:string"/>

            </xsd:sequence>

        </xsd:complexType>

       

    </xsd:schema>

  </wsdl:types>

 

  <wsdl:messagename="add">

    <wsdl:partelement="tns:add"name="parameters"/>

  </wsdl:message>

  <wsdl:messagename="addResponse">

    <wsdl:partelement="tns:addResponse"name="return"/>

  </wsdl:message>

 

  <wsdl:messagename="divide">

    <wsdl:partelement="tns:divide"name="parameters"/>

  </wsdl:message>

  <wsdl:messagename="divideResponse">

    <wsdl:partelement="tns:divideResponse"name="return"/>

  </wsdl:message>

 

  <wsdl:messagename="DivideZeroException">

    <wsdl:partelement="tns:DivideZeroException"name="fault"/>

 </wsdl:message>

 

  <wsdl:portTypename="cal">

    <wsdl:operationname="add">

      <wsdl:inputmessage="tns:add"/>

      <wsdl:outputmessage="tns:addResponse"/>

    </wsdl:operation>

   

     <wsdl:operationname="divide">

      <wsdl:inputmessage="tns:divide"/>

      <wsdl:outputmessage="tns:divideResponse"/>

      <wsdl:faultname="DivideZeroException"message="tns:DivideZeroException"/>

    </wsdl:operation>

  </wsdl:portType>

 

  <wsdl:bindingname="calSOAP"type="tns:cal">

    <soap:bindingstyle="document"transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operationname="add">

      <soap:operationsoapAction=""/>

      <wsdl:input>

        <soap:bodyuse="literal"/>

      </wsdl:input>

      <wsdl:output>

        <soap:bodyuse="literal"/>

      </wsdl:output>

    </wsdl:operation>

   

    <wsdl:operationname="divide">

      <soap:operationsoapAction=""/>

      <wsdl:input>

        <soap:bodyuse="literal"/>

      </wsdl:input>

      <wsdl:output>

        <soap:bodyuse="literal"/>

      </wsdl:output>

      <wsdl:faultname="DivideZeroException">

          <soap:faultname="DivideZeroException"use="literal"/>

     </wsdl:fault>

    </wsdl:operation>

   

  </wsdl:binding>

  <wsdl:servicename="CalculationService">

    <wsdl:portbinding="tns:calSOAP"name="CalculationPort">

      <soap:addresslocation="http://127.0.0.1:8080/cal"/>

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

 

/**

  对应的接口方法中抛出异常

 */

@WebService(targetNamespace= "http://www.ssl.org/cal/")

public interface CalService

{

    

     @WebMethod

     @WebResult(name = "addResponse", targetNamespace = "")

     @RequestWrapper(localName ="add", targetNamespace ="http://www.ssl.org/cal/")

     @ResponseWrapper(localName ="addResponse", targetNamespace ="http://www.ssl.org/cal/")

     public int add(@WebParam(name = "a", targetNamespace = "") inta,

                      @WebParam(name ="b", targetNamespace ="") intb);

      

    

     @WebMethod

     @WebResult(name = "divideResponse", targetNamespace = "")

     @RequestWrapper(localName ="divide", targetNamespace ="http://www.ssl.org/cal/")

     @ResponseWrapper(localName ="divideResponse", targetNamespace ="http://www.ssl.org/cal/")

     public int divide(@WebParam(name = "a", targetNamespace = "") inta,

                         @WebParam(name ="b", targetNamespace ="") intb) throwsDivideZeroException;

 

}

 

注意web服务抛出的异常,要直接继承Exception类,而不能继承RuntimeException,若继承RuntimeException抛出异常时,异常信息不仅会传输到客户端,同时异常也会在服务端继续向上抛出。

6、       Header

对于soap消息来说,header不是必须的。但是header常常用来传输一些用户认证信息等,若要使用header传输数据,需要在wsdl中添加<wsdl:message>元素,在需要header信息的binding元素对应的方法中添加header,如下

<?xmlversion="1.0"encoding="UTF-8"standalone="no"?>

<wsdl:definitionsxmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

                  xmlns:tns="http://www.ssl.org/cal/"

                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"

                  name="CalculationService"

                  targetNamespace="http://www.ssl.org/cal/">

  <wsdl:types>

    <xsd:schematargetNamespace="http://www.ssl.org/cal/">

        <xsd:elementname="add"type="tns:add"/>

        <xsd:elementname="addResponse"type="tns:addResponse"/>

        <xsd:elementname="divide"type="tns:divide"/>

        <xsd:elementname="divideResponse"type="tns:divideResponse"/>

        <xsd:elementname="authInfo" type="tns:authInfo"/>

        <xsd:elementname="DivideZeroException"type="tns:DivideZeroException"/>

       

        <xsd:complexTypename="add">

            <xsd:sequence>

               <xsd:elementname="a"type="xsd:int"/>

               <xsd:elementname="b"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

        <xsd:complexTypename="addResponse">

            <xsd:sequence>

                 <xsd:elementname="addResponse"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

       

          <xsd:complexTypename="divide">

            <xsd:sequence>

               <xsd:elementname="a"type="xsd:int"/>

               <xsd:elementname="b"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

        <xsd:complexTypename="divideResponse">

            <xsd:sequence>

                 <xsd:elementname="divideResponse"type="xsd:int"/>

            </xsd:sequence>

        </xsd:complexType>

       

        <xsd:complexTypename="authInfo">

             <xsd:sequence>

                 <xsd:elementname="authInfo"type="xsd:string"/>

             </xsd:sequence>

        </xsd:complexType>

       

        <xsd:complexTypename="DivideZeroException">

            <xsd:sequence>

                <xsd:elementname="message"type="xsd:string"/>

            </xsd:sequence>

        </xsd:complexType>

       

    </xsd:schema>

  </wsdl:types>

 

  <wsdl:messagename="add">

    <wsdl:partelement="tns:add"name="parameters"/>

  </wsdl:message>

  <wsdl:messagename="addResponse">

    <wsdl:partelement="tns:addResponse"name="return"/>

  </wsdl:message>

 

  <wsdl:messagename="divide">

    <wsdl:partelement="tns:divide"name="parameters"/>

  </wsdl:message>

  <wsdl:messagename="divideResponse">

    <wsdl:partelement="tns:divideResponse"name="return"/>

  </wsdl:message>

 

  <wsdl:messagename="authInfo">

       <wsdl:partelement="tns:authInfo"name="authInfo"/>

 </wsdl:message>

 

  <wsdl:messagename="DivideZeroException">

     <wsdl:partelement="tns:DivideZeroException"name="fault"/>

  </wsdl:message>

 

  <wsdl:portTypename="cal">

    <wsdl:operationname="add">

      <wsdl:inputmessage="tns:add"/>

      <wsdl:outputmessage="tns:addResponse"/>

    </wsdl:operation>

   

     <wsdl:operationname="divide">

      <wsdl:inputmessage="tns:divide"/>

      <wsdl:outputmessage="tns:divideResponse"/>

      <wsdl:faultname="DivideZeroException"message="tns:DivideZeroException"/>

    </wsdl:operation>

  </wsdl:portType>

 

  <wsdl:bindingname="calSOAP"type="tns:cal">

    <soap:bindingstyle="document"transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operationname="add">

      <soap:operationsoapAction=""/>

      <wsdl:input>

        <soap:headeruse="literal"part="authInfo"message="tns:authInfo"/>

        <soap:body  use="literal"/>

     </wsdl:input>

      <wsdl:output>

        <soap:bodyuse="literal"/>

      </wsdl:output>

    </wsdl:operation>

   

    <wsdl:operationname="divide">

      <soap:operationsoapAction=""/>

      <wsdl:input>

        <soap:bodyuse="literal"/>

      </wsdl:input>

      <wsdl:output>

        <soap:bodyuse="literal"/>

      </wsdl:output>

      <wsdl:faultname="DivideZeroException">

          <soap:faultname="DivideZeroException"use="literal"/>

      </wsdl:fault>

    </wsdl:operation>

   

  </wsdl:binding>

  <wsdl:servicename="CalculationService">

    <wsdl:portbinding="tns:calSOAP"name="CalculationPort">

      <soap:addresslocation="http://127.0.0.1:8080/cal"/>

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

 

获取头信息有两种方法:

l  在接口方法中显示声明字段信息为头信息,如下

public int add(

@WebParam(name="authInfo",header=true) String authInfo,

@WebParam(name = "a", targetNamespace = "") inta,

@WebParam(name = "b", targetNamespace = "") intb);

l  通过handler获取头部信息,handler相当于过滤器或拦截器;

7、       Handler

在web服务中,handler类似于servlet中的过滤器。当请求达到时,首先会被过滤器处理,之后才达到提供服务的Servlet。相应的,web服务中的handler通常会进行下列处理:

l  对客户端进行认证、授权;

l  日志记录;

l  对soap消息的加密和解密等;

在web服务中,handler既可以部署在服务器端,也可以部署在客户端。Handler有两类:一种是用来访问整个SOAP消息的SOAPHandler;另一种是用来访问SOAP消息的payload(SOAP 消息body部分)的LogicalHandler。在实际使用中,往往使用SOAPHandler来实现对消息的过滤,使用步骤如下:

1)     实现SOAPHandler<SOAPMessageContext>

2)     编写handler-chain.xml文件

3)     在服务实现类中添加@HandlerChain(file=””)注解

7.1、实现Handler接口

在第六章中,我们为CalculationService的add方法添加了authInfo头信息,并采用参数来接收authInfo信息。现在我们采用Handler的方法来处理头信息(相关api请查阅jax-wsapi),如下

public class AuthHandler implements SOAPHandler<SOAPMessageContext>

{

     @Override

     public boolean handleMessage(SOAPMessageContext context)

     {

          try

          {

               Boolean out=(Boolean)context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);

               if (!out)

               {

                     SOAPEnvelope envelope=context.getMessage().getSOAPPart().getEnvelope();

                     SOAPBody body=envelope.getBody();

                     String elementName=body.getChildNodes().item(0).getLocalName();

                     if ("add".equals(elementName))

                     {

                         SOAPHeader header=envelope.getHeader();

                         if (header!=null)

                         {

                             String authInfo=header.getTextContent();

                             System.out.println("authInfo="+authInfo);

                         }

                     }

               }

               return true;

          }

          catch (Exceptione)

          {

              

          }

          return false;

     }

 

     @Override

     public boolean handleFault(SOAPMessageContext context)

     {

          return false;

     }

 

     @Override

     public void close(MessageContext context)

     {

         

     }

 

     @Override

     public Set<QName> getHeaders()

     {

          return null;

     }

   

}

7.2、handler-chain.xml文件

多个handler可以组成一个链,在handler-chain.xml文件中可以添加多个handler,如下:

<?xml version="1.0" encoding="UTF-8"?>

<handler-chains xmlns="http://java.sun.com/xml/ns/javaee"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"

  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

 http://java.sun.com/xml/ns/javaee/javaee_web_services_metadata_handler_2_0.xsd">

     <handler-chain>

            <handler>

                  <handler-name>AuthInfo</handler-name>

                  <handler-class>handler.AuthHandler</handler-class>

            </handler>

     </handler-chain>

</handler-chains>

7.3、@HandlerChain

在服务实现类上添加@Handler注解,如下

@WebService(endpointInterface="org.ssl.cal.CalService",

            targetNamespace="http://www.ssl.org/cal/",

            portName="CalculationPort",

            serviceName="CalculationService",

            wsdlLocation="META-INF/cal.wsdl")

@HandlerChain(file="META-INF/handler-chain.xml")

public class CalServiceImpl  implements CalService

{

    //.......

}

8、       ws-security

ws-security是一种提供web服务上应用安全方法的网络传输协议,描述了如何将签名和加密头加入SOAP消息,记忆如何在消息中国加入安全令牌等。

 

9、       servlet发布

以上章节中的web服务都是基于JavaSE发布,那么如何将web服务发布到web容器呢?

将web服务发布到servlet容器,需要借助jaxws-rt(JAX WSRI Runtime Bundle),sun的jaxws-r提供了两个类用来实现url到webservice实现类的映射:

l  Listener:WSServletContextListener

l  Servlet: WSServlet

监听器和servlet的作用如下所示


监听器WSServletContextListener会监听每个请求,然后查找WEB-INF下的sun-jaxws.xml,若请求路径匹配,则转交给WSServlet进行处理。

将web服务发布到servlet容器步骤如下:

1.       添加jaxws-rt及其依赖包,建议采用Maven管理依赖包

2.       添加sun-jaxws.xml文件

在WEB-INF根目录下添加sun-jaxws.xml文件(与web.xml同一目录下),文件内容如下

<?xml version="1.0" encoding="UTF-8"?>

<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">

<endpoint name="UserService"

implementation="com.ssl.UserServiceImpl"        url-pattern="/user"/>

</endpoints>

注意:

l  endpoint标签的name属性必须和wsdl文件中的Service Name一致,implementation指明服务实现类,url-pattern指明webservice路径;

l  此时,schema,wsdl等文件一般放置在WEB-INF/wsdl目录下;

3.       配置Listener、Servlet

在web.xml中添加jaxws-rt提供的Listener和Servlet,如下

<listener>

<listener-class>

 com.sun.xml.ws.transport.http.servlet.WSServletContextListener

</listener-class>

</listener>

    

<servlet>

  <servlet-name>UserService</servlet-name>

<servlet-class>

com.sun.xml.ws.transport.http.servlet.WSServlet

</servlet-class>

</servlet>

<servlet-mapping>

     <servlet-name>UserService</servlet-name>

    <url-pattern>/user</url-pattern>

</servlet-mapping>

9.1、servlet api

web服务发布在servlet容器中,那么如何在handler中访问servletapi呢? 可以采用Filter和ThreadLocal来实现,思路如下:

l  Filter用来过滤请求,以获取相关的数据等;

l  ThreadLocal用来保存Filter获取的数据,这些数据都是与线程相关的,所以采用ThreadLocal来保存这些数据;

l  在handler中,通过ThreadLocal获取与本线程相关的数据。

/**

  * 保存用户数据,采用ThreadLocal实现

 * */

public class UserContext

{

   /**

    * 线程封闭

    * */

   private  staticThreadLocal<User> userContext=new ThreadLocal<User>();

  

   public static User getUser()

   {

        returnuserContext.get();

   }

  

   public static void setUser(User user)

   {

        userContext.set(user);

   }

   public static void removeUser()

   {

        userContext.remove();

   } 

}

/**

  * 过滤请求

 * */

public class StudentWSFilter implements Filter

{

 

     public void destroy()

     {

     }

 

     public void doFilter(ServletRequest request, ServletResponseresponse,

               FilterChain filterChain)throws IOException, ServletException

     {

          try

          {

           System.out.println("ws-filter");

               HttpServletRequest r=(HttpServletRequest)request;

               User user=(User)r.getSession().getAttribute("user");

               if (user!=null)

               {

                     System.out.println("set user");

                     UserContext.setUser(user);

               }

               filterChain.doFilter(request,response);

          }

          catch (Exceptione)

          {

              

          }

         finally

         {

          /**

             * 数据使用过后必须清除

             * */  

           System.out.println("removeuser");

           UserContext.removeUser();

         }

     }

 

     public void init(FilterConfig filterConfig)throws ServletException

     {

     }

 

}

10、   spring整合

web 服务与spring整合分为服务端整合和客户端整合。

10.1、服务器端

服务器端整合,就是让web服务的实现类与其他组件一样处于spring的容器中,以便可以向web服务中注入其他组件。

web服务与spring整合需要借助于jaxws-spring,其整合步骤如下

1)     添加jaxws-spring及其依赖包,建议采用Maven管理依赖包

2)     此外添加spring配置文件jaxws-beans文件以配置web服务的bean

在jaxws-beans文件中,配置web服务时,需要引入jaxws-spring相关的schema文件,下面以jaxws-spring-1.8为例说明:

<?xml version="1.0" encoding="UTF-8"?>

<beans  xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:ws="http://jax-ws.dev.java.net/spring/core"

        xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"

        xsi:schemaLocation="http://www.springframework.org/schema/beans

          http://www.springframework.org/schema/beans/spring-beans-4.0.xsd

           http://jax-ws.dev.java.net/spring/core

          http://jax-ws.dev.java.net/spring/core.xsd

        http://jax-ws.dev.java.net/spring/servlet

        http://jax-ws.dev.java.net/spring/servlet.xsd">

    

   <!--采用spring容器管理webservice-->

    <wss:binding url="/ss">

      <wss:service>

           <ws:service bean="#studentWSService">

                 <ws:handlers>

                       <beanclass="com.ssl.student.ws.handler.AuthHandler"/>

               </ws:handlers>

                 <ws:metadata>

                          <value>/WEB-INF/wsdl/student.xsd</value>

                     </ws:metadata>

           </ws:service>

      </wss:service>

    </wss:binding>

3)     在web.xml文件中配置WSSpringServlet

 

<!-- WSSpringServlet来管理ws,使web servicespring容器管理 -->

<servlet>

     <servlet-name>StudentWSService</servlet-name>

     <servlet-class>

     com.sun.xml.ws.transport.http.servlet.WSSpringServlet

    </servlet-class>

</servlet>

<servlet-mapping>

     <servlet-name>StudentWSService</servlet-name>

     <url-pattern>/ss</url-pattern>

</servlet-mapping>

 

注意:

jaxws-springschema文件地址已发生改变,若发生schema文件未找到,请尝试将文件地址改为
http://jax-ws.java.net/spring/core     http://jax-ws.java.net/spring/core.xsd     http://jax-ws.java.net/spring/servlet     http://jax-ws.java.net/spring/servlet.xsd

l   此时,配置handler,需要在<ws:service>中配置;

l   web服务实现类最好在jaxws-beans文件中配置,应采用统一管理的方式,而不要采用注解方式;

l   由于web服务所需要的bean是单独在jaxws-beans文件中的,若该文件中的bean依赖其他配置文件中的bean,注意spring配置文件的加载顺序;

l   此时不再需要sun-jaxws.xml文件;

10.2、servlet api

web服务与spring整合后,可以在web服务实现类中方便的获取servlet对象,获取servlet对象的步骤如下:

(1)在webu服务实现类中,让spring注入WebServiceContext对象

 @Resource

     private WebServiceContextwebServiceContext;

(2)在实现类的相关方法中通过WebServiceContext对象获取servelt对象

 //获取Servlet原生API

MessageContext messageContext=webServiceContext.getMessageContext();

HttpServletRequest request=

(HttpServletRequest) messageContext.get(SOAPMessageContext.SERVLET_REQUEST);

 

10.4、客户端

客户端与spring整合比较简单,如下

<!-- 使用spring来注入一个wsclient -->

     <bean id="studentWSService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">

         <property name="serviceInterface" value="com.ssl.student.StudentWSService"/>

         <property name="wsdlDocumentUrl" value="http://127.0.0.1:8080/stu/ss?wsdl"/>

         <property name="namespaceUri" value="http://www.ssl.com/student/"/>

         <property name="serviceName" value="StudentWSService"/>

         <property name="portName" value="StudentWSPort"/>

         <!--设置Handler-->

         <property name="handlerResolver" ref="authHandlerResolver"/>

  </bean>

需要注意的是,客户端与spring整合,需要添加handler,需要实现javax.xml.ws.handler.HandlerResolver接口,该接口用来返回Handler列表。

 

11、   实例

  

12、   附录


 

12.1、SAX

当XML文档作为数据交换工具时,应用程序必须采用合适的方式来获取XML文档的有用信息,这就需要解析XML文档了。为了利用XML的结构化特性进行解析,有2种比较流行的解析方式:

l  DOM ,文档对象模型,由W3C推荐处理XML文档的规范;

l  SAX(Simple API for XML)

DOM 为解析XML文档定义了一组标准接口,DOM解析器负责读入整个文档,然后将该文档转换成常驻内存的树状结构。

DOM标准简单易用,但有一个显著的问题:它需要一次性地读取整个文档,而且运行期间,整棵DOM树常驻内存,导致系统开销过大。SAX正是为了解决DOM问题而出现的一套标准,SAX采用事件驱动机制来解析XML文档。

每当SAX解析器发现文档开始、元素开始、元素结束、文本和文档结束等事件时,就会向外发送一次事件,而程序员则通过编写事件监听器监听这些这些事件来获取XML文档中的信息。SAX解析方式占用内存极小,速度更快。

JAXP:JavaAPI for XML,是建立在DOM和SAX之上的一个抽象层,具体信息请参考JavaAPI。

 

12.2、JAXB

Java Architecture For XML inding ,实现xml格式数据和Java对象之间的转换,从而支持数据绑定功能。

 

12.3、JAX-WS

Java API For XML Web Service,是Java语言对xml web service的规范。

 

12.4、WSDL

Web Services Description Language,web服务描述语言,通俗地说,wsdl文档描述了webservice的如下3个方面:

l  WHAT:该web service包含“什么”操作;

l  HOW:该web service的操作应该“怎么”调用;

l  WHERE:该web service的服务地址;

12.5、SOAP

Simple Object Access Protocol,简单对象访问协议,一种基于XML的简易协议,允许程序通过HTTP来交换信息,用在分散或分布的环境中交换信息。

12.6、UDDI

UDDI(UniversalDescription Discovery and Integration,即统一描述、发现和整合协议)是一套信息注册规范,具有如下特点:

l  基于web

l  分布式

UDDI包括一组允许企业向外注册Web服务,以使其他企业发现、访问的实现标准。UDDI核心是UDDI注册中心,它使用XML文档来描述企业及其提供的Web服务。

通过UDDI,web服务提供者可以向UDDI注册中心注册Web服务,从而允许其他企业来访问该企业注册的Web服务。Web服务使用者可以通过UDDI注册中心来查找、发现自己所需要的服务,找到所需要的服务后,可以将自己绑定到指定的web服务提供者,在根据web服务对应的wsdl文档来调用对方的服务,其大致运行模式如下所示:

 

 

12.7、SOA

SOA(Serviceoriented Arichitecture),面向服务的架构。IBM的定义如下:面向服务的体系结构是一个组件模型,它将应用程序的不同功能单元称为服务。通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言,这使得构建在各种各样的系统中的服务可以以统一和通用的方式进行交互。

12.8、ESB

 

ESB(EnterpriseService Bus)企业服务总线,类似于电脑总线,负责各个组件之间的通信。其核心思想是基于消息中间件来实现系统间的交互。基于消息中间件所构建的系统交互的中间场所称为总线,系统间交互的数据格式采用统一的消息格式,由总线完成消息的转化、路由、发送等,基于ESB构建的系统如下图所示:

 

0 0
原创粉丝点击