3.3.2. 开发 JAX-WS 客户端应用程序
客户端与 JAX-WS 端点通信和请求工作,该端点部署在 Java 企业版 7 容器中。有关下面提及的类、方法和其他实施详细信息,请查看 JBoss EAP 中包含的 Javadocs 捆绑包的相关部分。
概述
服务 是一种抽象,代表 WSDL 服务。WSDL 服务是相关端口的集合,各自包括绑定到特定协议的端口类型和特定的端点地址。
通常,当从现有的 WSDL 合同生成其余组件存根时,服务会生成。可以通过部署的端点的 WSDL URL 获取 WSDL 合同,也可以使用 EAP_HOME/bin/ 目录中的 wsprovide 工具从端点来源创建。
这种类型的用法被称为静态用例。在这种情况下,您要创建 Service 类的实例,它作为组件 stubs 之一创建。
您还可以使用 Service.create 方法手动创建服务。这称为动态用例。
使用
静态用例
JAX-WS 客户端的静态用例假定您已有 WSDL 合同。这可以通过外部工具生成,或者在创建 JAX-WS 端点时使用正确的 JAX-WS 注释生成。
若要生成组件存根,可使用 EAP_HOME/bin 中包含的 wsconsume 工具。工具将 WSDL URL 或文件取为参数,并生成多个文件,这些文件结构在一个目录树中。代表您的 服务的 源和类文件分别命名为 _Service.java 和 _Service.class。
所生成的实施类具有两个公共构造器,一个没有参数,另一个具有两个参数。这两个参数分别代表 WSDL 位置( java.net.URL)和服务名称( javax.xml.namespace.QName)。
no-gument 构造器是最常使用的。在这种情况下,WSDL 位置和服务名称可在 WSDL 中找到。它们从 @WebServiceClient 注释隐式设置,该注释用于解密生成的类。
@WebServiceClient(name="StockQuoteService", targetNamespace="http://example.com/stocks", wsdlLocation="http://example.com/stocks.wsdl")
public class StockQuoteService extends javax.xml.ws.Service
{
public StockQuoteService() {
super(new URL("http://example.com/stocks.wsdl"), new QName("http://example.com/stocks", "StockQuoteService"));
}
public StockQuoteService(String wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
...
}
有关如何从服务获取端口以及如何在该端口上调用操作的详情,请参阅 Dynamic Proxy。有关如何直接使用 XML 有效负载或使用整个 SOAP 消息的 XML 表示的详情,请参阅 Dispatch。
动态用例
在动态情况下,不会自动生成 stubs。相反,Web 服务客户端使用 Service.create 方法来创建 服务 实例。以下代码片段说明了此过程。
URL wsdlLocation = new URL("http://example.org/my.wsdl");
QName serviceName = new QName("http://example.org/sample", "MyService");
Service service = Service.create(wsdlLocation, serviceName);
处理程序 Resolver
JAX-WS 为消息处理模块(称为处理程序)提供灵活的插件框架。这些处理程序扩展了 JAX-WS 运行时系统的功能。Service 实例通过一对 getHandler 的访问,它们可以在每个服务、每个端口或协议绑定的基础上配置一组处理程序。
Resolver 和 ResolversetHandlerResolver 方法提供对 Handler
当 服务 实例创建代理或 Dispatch 实例时,当前注册到该服务的处理程序解析器会创建所需的处理程序链。对为 Service 实例配置的处理器解析器的后续更改不会影响之前创建的代理或 Dispatch 实例上的处理程序。
执行器
服务 实例可以使用 java.util.concurrent.Executor 配置。Executor 调用应用请求的任何异步回调。setExecutor 和 getExecutor 方法可以修改和检索为服务配置的 Executor。
动态代理
动态代理是使用 服务 中提供的其中一个 getPort 方法 的客户端代理实例。portName 指定服务使用的 WSDL 端口的名称。serviceEndpointInterface 指定创建的动态代理实例支持的服务端点接口。
public <T> T getPort(QName portName, Class<T> serviceEndpointInterface)
public <T> T getPort(Class<T> serviceEndpointInterface)
服务端点接口通常使用 ws Consume 工具生成,该工具 解析 WSDL 并从中创建 Java 类。
还提供了返回端口的 typed 方法。这些方法也会返回实施 SEI 的动态代理。请参见以下示例。
@WebServiceClient(name = "TestEndpointService", targetNamespace = "http://org.jboss.ws/wsref",
wsdlLocation = "http://localhost.localdomain:8080/jaxws-samples-webserviceref?wsdl")
public class TestEndpointService extends Service {
...
public TestEndpointService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
@WebEndpoint(name = "TestEndpointPort")
public TestEndpoint getTestEndpointPort() {
return (TestEndpoint)super.getPort(TESTENDPOINTPORT, TestEndpoint.class);
}
}
@WebServiceRef
@WebServiceRef 注释声明了对 Web 服务的引用。它遵循 JSR 250 中定义的 javax.annotation.Resource 注释所显示的资源模式。与这些注解对应的 Jakarta EE 遵循 Jakarta Annotations 1.3 规范。
-
您可以使用它来定义类型为生成的
Service类的引用。在本例中,type 和 value 元素各自引用生成的Service类类型。此外,如果引用类型可以通过字段或方法声明推断,则该注解将应用到,则类型和值元素可能具有Object.class的默认值,但不是必须的。如果无法推断类型,则至少必须使用非默认值存在 type 元素。 您可以使用它来定义类型为 SEI 的引用。在这种情况下,如果可以从注解的字段或方法声明推断引用的类型,则 type 元素可能会(但不需要)使用默认值。但是,value 元素必须始终存在并引用生成的服务类类型,这是
javax.xml.ws.Service的子类型。wsdlLocation元素将覆盖所引用服务类的@WebService注释中指定的 WSDL 位置信息。public class EJB3Client implements EJB3Remote { @WebServiceRef public TestEndpointService service4; @WebServiceRef public TestEndpoint port3; }
分配
XML Web 服务使用 XML 消息在端点和任何客户端之间进行通信。XML 消息使用名为 Simple Object Access Protocol(SOAP)的 XML 语言。JAX-WS API 为端点和客户端提供了机制,让它们分别能够发送和接收 SOAP 消息。marshalling 是将 Java 对象转换为 SOAP XML 消息的过程。解压是将 SOAP XML 消息转换回 Java 对象的过程。
在某些情况下,您需要访问原始 SOAP 消息本身,而不是转换的结果。Dispatch 类提供此功能。分配 以两种使用模式之一运行,由以下任一常量标识:
-
javax.xml.ws.Service.Mode.MESSAGE- 此模式指示客户端应用直接使用特定于协议的消息结构。与 SOAP 协议绑定一起使用时,客户端应用程序直接与 SOAP 消息配合工作。 -
javax.xml.ws.Service.Mode.PAYLOAD- 此模式使客户端能够处理载荷本身。例如,如果与 SOAP 协议绑定搭配使用,客户端应用程序将处理 SOAP 正文的内容,而不是整个 SOAP 消息。
分配 是一个低级别 API,要求客户端将消息或载荷构建为 XML,并严格遵守各个协议的标准以及对消息或有效载荷结构的详细了解。分配 是一个通用类,支持任何类型的消息或消息有效负载的输入和输出。
Service service = Service.create(wsdlURL, serviceName);
Dispatch dispatch = service.createDispatch(portName, StreamSource.class, Mode.PAYLOAD);
String payload = "<ns1:ping xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
dispatch.invokeOneWay(new StreamSource(new StringReader(payload)));
payload = "<ns1:feedback xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
Source retObj = (Source)dispatch.invoke(new StreamSource(new StringReader(payload)));
异步调用
BindingProvider 接口代表提供客户端可以使用的协议绑定的组件。它由代理实施,并由 Dispatch 接口扩展。
BindingProvider 实例可能会提供异步操作功能。异步操作调用在调用时与 BindingProvider 实例分离。操作完成后不会更新响应上下文。相反,可以使用 Response 接口提供单独的响应上下文。
public void testInvokeAsync() throws Exception {
URL wsdlURL = new URL("http://" + getServerHost() + ":8080/jaxws-samples-asynchronous?wsdl");
QName serviceName = new QName(targetNS, "TestEndpointService");
Service service = Service.create(wsdlURL, serviceName);
TestEndpoint port = service.getPort(TestEndpoint.class);
Response response = port.echoAsync("Async");
// access future
String retStr = (String) response.get();
assertEquals("Async", retStr);
}
@Oneway Invocations
@Oneway 注释表示给定 Web 方法采用输入消息,但不返回任何输出消息。通常,@Oneway 方法在执行业务方法之前,将控制线程返回到调用应用。
@WebService (name="PingEndpoint")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class PingEndpointImpl {
private static String feedback;
@WebMethod
@Oneway
public void ping() {
log.info("ping");
feedback = "ok";
}
@WebMethod
public String feedback() {
log.info("feedback");
return feedback;
}
}
超时配置
两种不同的属性控制 HTTP 连接的超时行为和等待接收消息的客户端的超时。第一个是 javax.xml.ws.client.connectionTimeout,第二个则是 javax.xml.ws.client.receiveTimeout。每个以毫秒为单位表示,正确语法如下所示:
public void testConfigureTimeout() throws Exception {
//Set timeout until a connection is established
((BindingProvider)port).getRequestContext().put("javax.xml.ws.client.connectionTimeout", "6000");
//Set timeout until the response is received
((BindingProvider) port).getRequestContext().put("javax.xml.ws.client.receiveTimeout", "1000");
port.echo("testTimeout");
}