加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

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

发布时间:2020-12-17 00:10:35 所属栏目:安全 来源:网络整理
导读:JAX-WS 快速入门 1. JAX-WS 简介 1)????????JAX-WS 2.0是一种新的开发 模式 。 2)????????JAX-WS 提供了一套标准(基于标签模式)来简化Web服务和客户端的开发。 3)????????JAX-WS 模式取代了 JAX-RPC ( Remote Procedure Call ) 的远程方法调用模式。 4)?

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服务(比如),并且可以并行开发service和服务所需的元数据

  • 异步调用

异步调用指客户端与服务器的异步通信。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客户端支持callback异步调用模式。

静态客户端也叫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)即一个服务端接口,但是这不是必须的,如果没有则可看做是内部然后准备好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,

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

3.??????创建Dispatch对象
Dispatch<SOAPMessage> dispatch =?
service.createDispatch(portName,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,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管理支持:

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);

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读