WebService从零到项目开发使用5—技术研究之JAX-WS快速入门

JAX-WS 快速入门

1. JAX-WS 简介

1)        JAX-WS 2.0是一种新的开发 模式

2)        JAX-WS 提供了一套标准(基于标签模式)来简化Web服务和客户端的开发。

3)        JAX-WS 模式取代了JAX-RPCRemote Procedure Call的远程方法调用模式。

4)        JAX-WS 模式是策略编程模式,依赖于Java(SE&EE)平台。

5)        JAX-WS 标准的实现者在开发Web服务和客户端方面上提供了很多增强。

  •  更好的平台依赖性

使用JAX-WS标准开发的Web服务和客户端能更好的依赖于以Java为平台的应用。比如,JAX-WS充分的利用了Java的动态Proxy模式,增强了JAX-RPC

  • 使用标注

JAX-WS使用标注指明Java类为Web服务。该功能依赖于Java EE5以上平台。

例如:

@WebService

public class QuoteBean implements StockQuote {

    public float getQuote(String sym) { ... }

}

@ WebService标注告诉服务器将该类的所有public方法暴露并将其作为一个WebService。使用标注有利于团队开发,你不必定义部署描述符来指定Web服务(比如JAX-RPC),并且可以并行开发service和服务所需的元数据

  • 异步调用

异步调用指客户端与服务器的异步通信。JAX-WS支持同步和异步调用。JAX-WS采用轮询机制(polling)和异步回调机制(Callback。使用polling模式,客户端先发送一个请求,获得一个response对象,然后客户端使用reponse对象去判断服务器是否已经响应了请求;如果使用Callback模式,客户端提供一个回调Handler去接受和处理发送回来的response对象。采用异步调用后客户端不必等待返回服务器的response而可以继续处理本地的业务,提高了系统效率。


下面有个异步调用的示例:(一个Service接口中应该包括处理同步和异步请求的方法)

@WebService

public interface CreditRatingService {

    // 处理同步请求的方法

    Score getCreditScore(Customer customer);

    // 采用polling模式来处理异步请求的方法

    Response<Score> getCreditScoreAsync(Customer customer);

    // 采用callback模式来处理异步请求的方法

    Future<?> getCreditScoreAsync(Customer customer, AsyncHandler<Score> handler);

}

下面是回调模式的处理Handler

// 客户端创建Service实例

CreditRatingService svc = ...;

// 客户端实现Handler

Future<?> invocation = svc.getCreditScoreAsync(customerFred,

         new AsyncHandler<Score>() {

           public void handleResponse (Response<Score> response) {

                          Score score = response.get();

                          // do work here...

                }

    }

);

下面是Polling模式的处理Handler

CreditRatingService svc = ...;

Response<Score> response = svc.getCreditScoreAsync(customerFred);

while (!response.isDone()) {

     // 等待服务器响应时做

     System.out.println("is not down");

}

Score score = response.get();

  • 使用资源注入

资源注入是指Java EE 5支持在运行时创建和初始化通用资源。JAX-WS使用这种技术将资源的创建任务从Service服务中分离出来并分配给相应的容器(Servlet容器)。

@WebService

public class MyService {

    @Resource

    private WebServiceContext ctx;

    public String echo (String input) {

    }

}

将@WebService标注放到服务端点的实现类上,我们就可以请求资源注入,然后通过javax.xml.ws.WebServiceContext接口获得指定的资源。

  • Java Architecture for XML Binding (JAXB) 2.0数据绑定技术

使用JAXB 2.0 API和工具完成Java对象和XML文档的相互转换即数据绑定过程。

  • 动态和静态客户端

Dispatch(javax.xml.ws.Dispatch)客户端使用JAX-WS的动态调用API,Dispatch客户端以PayloadMessage模式发送XML消息。使用Payload模式时Dispatch客户端仅负责提供<soap:Body>的内容,JAX-WS会添加<soap:Envelope>和 <soap:Header>元素;如果使用Message模式,则Dispatch客户端负责提供整个SOAP外壳(包括<soap:Envelope>,<soap:Header>, 和 <soap:Body>),JAX-WS不会在SOAP消息中添加任何内容。Dispatch客户端支持callbackpolling异步调用模式。

静态客户端也叫Proxy代理客户端,代理客户端如果要调用远程Service则必须为其提供一个Service Endpoint interface (SEI),客户端使用SEI进行远程调用,可用JAX-WS命令工具生成实现类。

  • 支持MTOM( MessageTransmission Optimized Mechanism)

使用JAX-WS,我们可以将附件以二进制base64的形式和Web服务请求一起发送给服务器,JAX-WS使用MTOM优化了对二进制数据的传输。

  • 命令行开发工具wsgen和wsimport

wsgen用于服务器端,使用一个class生成相应的Service和WSDL文件,然后部署去服务;wsimport主要用于客户端,根据WSDL文件生成SEI Java文件以及用于匹配XML的JAXB2.0的类文件。Wsimport生成的是客户端动态Proxy

2. JAXB 简介

1)        Java Architecture for XMLBinding (JAXB) 2.0数据绑定技术,是一套标准API,用于映射Java类和XML Document,支持运行时绑定。

2)        JAXB是JAX-WS 2.0默认使用的数据绑定技术。

3)        Axis2提供支持JAXB 2.0标准API。

4)        JAXB的结构模型

3. 开发JAX-WS 服务

3.1. 使用JavaBean开发JAX-WS服务(从下向上)

使用已有的JavaBean去开发JAX-WS服务,添加@WebService标注将Bean定义为一个Web服务,JavaBean可以有一个SEIserviceendpoint interface)即一个服务端接口,但是这不是必须的,如果没有则可看做是内部SEI然后准备好Web服务所需的所有class和文件,将应用发布到Axis2。在这里,我们不必去开发一个WSDL文件,因为标注的使用会提供所有的WSDL信息来配置服务端或者是客户端,但做项目时最好还是有WSDL文件。下面是我采用的开发步骤。

1.      开发一个SEI实现类
有两种方式来开发:标准的JavaBean SEI和Provider接口(XML消息级别处理)。
标准的JavaBean SEI使用@WebService,Provider接口使用@WebServiceProvider。

如果JavaBean Service实现类使用SEI接口,则实现类必须通过endpointInterface引用SEI接口,如果实现类没有使用SEI接口,则必须将在内容来定义和描述SEI。示例如下:

import javax.jws.WebMethod;

import javax.jws.WebService;

import javax.jws.soap.SOAPBinding;

import javax.jws.soap.SOAPBinding.Style;

@WebService

@SOAPBinding(style = Style.RPC)

public interface AuthHello {

         @WebMethod

         public String say(String name);

}

        

import javax.jws.WebService;

@WebService(endpointInterface = "my.ws.fromjava.AuthHello")

public class AuthHelloImpl implements AuthHello {

         @Override

         public String say(String name) {

                   return "hello: " + name;

         }

}

2.      当使用从下向上,从SEI实现类开始开发时,我们使用wsgen命令行工具来生成部分服务组件。这些组件有:

  • JAXB类文件:处理消息,数据绑定,其中包含@RequestWrapper包装的方法。
  •  WSDL文件:除了使用此命令外,WSDL文件在发布服务时可以自动生成。

注意当使用从下向上从现有Bean开发Web服务时,我们不必要去生成一个WSDL文件,JAX-WS的标注已经提供了所有WSDL信息来配置我们的Service

如果想发布实现类从父类集成的方法,则需要将父类用@WebService标注上。

3.      准备打包和发布服务。

3.2. 使用WSDL文件开发JAX-WS服务(从上向下)

1.      使用已有的WSDL文件去开发JAX-WS服务,使用wsimport工具。我们使用wsimport命令行工具来生成部分服务组件。这些组件有:

  • SEI:Serviceendpoint interface
  • 服务实现类:SEI实现类
  • 异常类:从wsdl:fault映射的类
  •  JAXB类型值

wsimport -keep -verbose wsdl_URL

   -keep:不允许删除生成的文件
   -verbose:列出生成的文件

2.      为生成的SEI提供自行开发的实现类。

4. 打包和部署JAX-WS服务

通过导入JAX-WS RIJar包的方式,我们可以在Servlet容器中使用监听器com.sun.xml.ws.transport.http.servlet.WSServletContextListener和sun-jaxws.xml来部署Web服务。

这里我们使用现有的ApacheAxis2框架来发布JAX-WS服务。比如打包为AAR文件然后上传到tomcat。具体方法可参照Axis2文章。

5. 开发JAX-WS 客户端

5.1. Dispatch和Proxy客户端

1.      JAX-WS支持两种方式开发客户端:Dispatchclient API、the Dynamic Proxy client API。

  • Dispatch:在XML消息级别使用,不需要额外生成JAX-WS组件。
  • Dynamic Proxy:基于一个SEI去调用Web服务。
  • Dispatch是动态方法调用模式,Proxy是动态代理模式。
  • 这两种模式都可以同步或异步调用Web服务。

2.      Dispatch client

Web服务器与客户端是通过XML消息进行通信的,很多时候,JAX-WS APIs隐藏了XML消息处理的底层细节。但是有时我们想在XML消息级别上开发,这时我们可以使用Dispatch client APIs。

客户端创建dispatch对象时可以传入以下几种实例类型:

  • javax.xml.transform.Source
  • JAXB objects
  • javax.xml.soap.SOAPMessage
  • javax.activation.DataSource

例如使用SOAPMessage:

QName serviceName = new QName(targetNamespace, _serviceName);

QName portName = new QName(targetNamespace, _portName);

javax.xml.ws.Service service = Service.create(serviceName);

service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING,

         "http://localhost:8080/Test/TestWebService?wsdl");

Dispatch<SOAPMessage> dispatch =

         service.createDispatch(portName,SOAPMessage.class, Service.Mode.MESSAGE);

BindingProvider bp = (BindingProvider) dispatch;

Map<String, Object> rc = bp.getRequestContext();

rc.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);

rc.put(BindingProvider.SOAPACTION_URI_PROPERTY, OPER_NAME);

MessageFactory factory = ((SOAPBinding) bp.getBinding()).getMessageFactory();

SOAPMessage request = factory.createMessage();

......

SOAPMessage reply = dispatch.invoke(request);

......

例如使用Sourse:

Dispatch<Source> dispatch = … create a Dispatch<Source>

Source request = … create a Source object

Source response = dispatch.invoke(request);

dispatch.invoke的调用方式:

l  使用invoke同步调用;

l  使用带callback或者polling的invokeAsync异步调用。

3.      Dynamic Proxy client

动态代理客户端必须先有一个SEI(从WSDL生成),然后Proxy基于SEI远程调用Web服务。动态Proxy类似RPC编程中的Stub(存根类),都是从WSDL生成的,但是Stub是静态的方式调用服务,而动态Proxy是基于JDK的动态代理模式、动态调用的方式。

dispatch.invoke的调用方式:

  • 使用invoke同步调用;
  •  使用带callback或者polling的invokeAsync异步调用。

5.2. 使用WSDL文件开发JAX-WS客户端

对于JAX-WS静态客户端编程模型是所谓的动态代理客户端。

创建动态代理客户端的wsimport命令行是:

wsimport -keep –b binding.xml -d bin -s src wsdl/TestService.wsdl

         以上使用binding.xml生成异步调用动态Proxy。

5.3. 使用JAX-WS APIs开发动态dynamic客户端

我们可以使用JAX-WS提供的动态调用接口Dispatch client API来开发客户端调用。

开发步骤是:

1.      选择动态客户端发送消息的模式:payload或message。

2.      创建Service实例,添加port。

service.addPort(portName,SOAPBinding.SOAP11HTTP_BINDING,

         "http://localhost:8080/Test/TestWebService?wsdl");

3.      创建Dispatch对象
Dispatch<SOAPMessage> dispatch = 
service.createDispatch(portName,SOAPMessage.class, Service.Mode.MESSAGE);

4.      配置request上下文属性

BindingProvider bp = (BindingProvider)dispatch;

Map<String, Object> rc =bp.getRequestContext();

rc.put(BindingProvider.SOAPACTION_USE_PROPERTY,Boolean.TRUE);

rc.put(BindingProvider.SOAPACTION_URI_PROPERTY,OPER_NAME);

5.      组装请求消息

6.      同步或异步发送消息

7.      处理相应消息

   String endpointUrl = ...;

               

   QName serviceName = new QName("http://org/apache/ws/axis2/sample/echo/",

    "EchoService");

   QName portName = new QName("http://org/apache/ws/axis2/sample/echo/",

    "EchoServicePort");

   /**创建Service实例,添加port **/

   Service service = Service.create(serviceName);

   service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);

               

   /** Create a Dispatch instance from a service.**/

   Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,

   SOAPMessage.class, Service.Mode.MESSAGE);

   /** Create SOAPMessage request. **/

   // compose a request message

   MessageFactory mf =
                MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);

   // Create a message.  This example works with the SOAPPART.

   SOAPMessage request = mf.createMessage();

   SOAPPart part = request.getSOAPPart();

   // Obtain the SOAPEnvelope and header and body elements.

   SOAPEnvelope env = part.getEnvelope();

   SOAPHeader header = env.getHeader();

   SOAPBody body = env.getBody();

   // Construct the message payload.

   SOAPElement operation = body.addChildElement("invoke", "ns1",

    "http://org/apache/ws/axis2/sample/echo/");

   SOAPElement value = operation.addChildElement("arg0");

   value.addTextNode("ping");

   request.saveChanges();

   /** Invoke the service endpoint. **/

   SOAPMessage response = dispatch.invoke(request);

 

   /** Process the response. **/

6. 使用异步的消息交换

默认情况下,异步客户端调用并没有通道上消息交换模式的异步行为,只是编程模式是异步的,客户端和服务器通信的request和response的消息交换不是异步的。

为了使用异步消息交换,必须在request上下文中设置属性org.apache.axis2.jaxws.use.async.mep为true。

Map<String, Object> rc = ((BindingProvider) port).getRequestContext();

rc.put("org.apache.axis2.jaxws.use.async.mep", Boolean.TRUE);

有了异步交换,request和response消息会添加WS-Addressing头 headers,它会提供额外的路由信息。

有了异步交换,response被传到一个异步Listener,然后再将response传递给客户端。此时不存在给客户端传递停止监听response的超时设置信息,我们只能通过cancel方法来强制客户端停止等待response。

使用异步交换后,对于polling和callback异步调用方式,最后需要通过在客户端调用cancel方法来取消response的等待,当然这样不会影响到服务器端对request的处理。

7. 在JAX-WS服务中使用Handlers

与JAX-RPC编程模式一样,JAX-WS也为应用程序提供了Handler机制来处理Message消息输入输出流(SOAP报文)。我们可以在JAX-WS运行时环境中添加Handlers而对请求和响应消息做额外的处理,比如安全和日志。JAX-WS中有两种使用Handler的方式,logical handler和protocol handler

1.      logical handler
独立于协议的逻辑Handler,能够获得流中的XML消息。需要实现接口javax.xml.ws.handler.LogicalHandler。一个逻辑Handler会接收LogicalMessageContext对象,logicalhandler基于SOAP 和 XML/HTTP-based。

2.      protocol handler
即协议Handler仅限于基于SOAP协议的配置,使用该Handler时必须实现接口javax.xml.ws.handler.soap.SOAPHandler,协议Handler会接收SOAPMessage 对象去读取消息数据。

JAX-WS运行时服务器端和客户端的Handler程序类没有区别。在服务器和客户端必须配置Handler,并且实现Handler内的特定业务逻辑。

为了使用Handler,需要在SEI或者实现类中添加@HandlerChain标注,然后添加一个对Handler描述的XML配置文件。对于客户端,我们也可以结合使用Bingding API以编程的方式来配置Handler。

@HandlerChain(file="../../common/handlers/myhandlers.xml")

或者

@HandlerChain(file="http://foo.com/myhandlers.xml")

 

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

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

   <!—注意:  * 表示一个通配符 -->

        <jws:handler-chain name="MyHandlerChain">

                <jws:protocol-bindings>##SOAP11_HTTP ##ANOTHER_BINDING</jws:protocol-bindings>

                <jws:port-name-pattern

               xmlns:ns1="http://handlersample.samples.apache.org/">ns1:MySampl*</jws:port-name-pattern>

           <jws:service-name-pattern

                 xmlns:ns1="http://handlersample.samples.apache.org/">ns1:*</jws:service-name-pattern>

                <jws:handler>

                        <jws:handler-class> samples.handlersample.SampleLogicalHandler</jws:handler-class>

                </jws:handler>

                <jws:handler>

                        <jws:handler-class> samples.handlersample.SampleProtocolHandler2</jws:handler-class>

                </jws:handler>

                <jws:handler>

                        <jws:handler-class> samples.handlersample.SampleLogicalHandler</jws:handler-class>

                </jws:handler>

                <jws:handler>

                        <jws:handler-class> samples.handlersample.SampleProtocolHandler2</jws:handler-class>

                </jws:handler>

        </jws:handler-chain>

   </jws:handler-chains>

 

   samples.handlersample;

import java.util.Set;

   import javax.xml.namespace.QName;

   import javax.xml.ws.handler.MessageContext;

   import javax.xml.ws.handler.soap.SOAPMessageContext;

   public class SampleProtocolHandler implements javax.xml.ws.handler.soap.SOAPHandler<SOAPMessageContext> {

       public void close(MessageContext messagecontext) {

       }

       public Set<QName> getHeaders() {

           return null;

       }

       public boolean handleFault(SOAPMessageContext messagecontext) {

           return true;

       }

       public boolean handleMessage(SOAPMessageContext messagecontext) {

           Boolean outbound = (Boolean) messagecontext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

           if (outbound.booleanValue()) {

               // 处理输出流

           } else(!outbound.booleanValue()) {

               // 处理输入流

           }

           return true;

       }

   }

8. 为JAX-WS提供HTTP Session管理支持

在Server端可以使用HTTPSession管理来维护用户状态信息,通过最小的信息返回给用户从而跟踪回话。我们可以使用session cookies 或 URL 重写来实现HTTP Session管理。

浏览器之间,应用服务器和应用程序之间的交互,对用户和应用程序都是透明的。应用程序和用户通常不知道服务器提供的会话标识符。

1.      session cookies
cookie是个客户端小文件负责维护JSESSIONID,发送请求时cookie用来匹配服务器端的session信息。从JAX-WS发送的请求,sessionID作为请求头的一部分传输,服务器根据特定的请求session ID匹配session信息,JAX-WS应用程序从HTTP响应头中获取Session ID,然后等下一请求时再发送给服务器。

2.      URL 重写
URL重写是指在URL重定向时将Seesion ID作为编码后的参数存储在URL中一起发送给服务器。这种编码的URL用于同一服务器的并发请求。使用步骤如下:

1)        配置服务器使之能够跟踪回话。

2)        在客户端通过设置JAX-WS属性javax.xml.ws.session.maintain为true来提供Session管理支持:

Map<String, Object> rc = ((BindingProvider) port).getRequestContext();

rc.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);

9. 使用MTOM

JAX-WS应用可以将二进制(base64or hexBinary编码)数据放在XML文档中发送。MTOM仅支持xs:base64Binary数据类型,MTOM默认下是不启动的。JAX-WS应用需要在服务器、客户端分别配置以提供MTOM支持。在服务器端仅可配置JavaBeans endpoint ,而不能配置provider-based endpoint。

1.      在服务端启动MTOM,在实现类上添加@BindingType标注以标示支持的数据绑定类型,javax.xml.ws.SOAPBinding类定义了两个常量SOAP11HTTP_MTOM_BINDING和 SOAP12HTTP_MTOM_BINDING。

// for SOAP version 1.1

@BindingType(value = SOAPBinding.SOAP11HTTP_MTOM_BINDING)

// for SOAP version 1.2

@BindingType(value = SOAPBinding.SOAP12HTTP_MTOM_BINDING)

2.      在客户端使用javax.xml.ws.soap.SOAPBinding client-side API来启动MTOM,客户端以两种方式启动MTOM向服务器发送二进制消息:(SOAP1.1)

l  Dispatch client:

// 方法1

SOAPBinding binding = (SOAPBinding)dispatch.getBinding();

binding.setMTOMEnabled(true);

// 方法2

Service svc = Service.create(serviceName);

svc.addPort(portName,SOAPBinding.SOAP11HTTP_MTOM_BINDING,endpointUrl);

l  Dynamic Proxy client:

// Create a BindingProvider bp from a proxy port.

Service svc = Service.create(serviceName);

MtomSample proxy = svc.getPort(portName, MtomSample.class);

BindingProvider bp = (BindingProvider) proxy;

//Enable MTOM

SOAPBinding binding = (SOAPBinding) bp.getBinding();

binding.setMTOMEnabled(true);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值