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 模式是策略编程模式,依赖于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客户端以Payload和Message模式发送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用于服务器端,使用一个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); |
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!