28.2. 实施一个 Consumer
概述
要在从 WSDL 合同开始时实施消费者,您必须使用以下 stubs:
- 服务类
- SEI
使用这些存根时,使用者代码会实例化服务代理来在远程服务上发出请求。它还实施使用者的业务逻辑。
生成的服务类
例 28.2 “生成的 Service Class 的概述” 显示生成的服务类 ServiceName_Service
的典型概述[2],它扩展了 javax.xml.ws.Service
基本类。
例 28.2. 生成的 Service Class 的概述
@WebServiceClient(name="..." targetNamespace="..." wsdlLocation="...") public class ServiceName extends javax.xml.ws.Service { ... public ServiceName(URL wsdlLocation, QName serviceName) { } public ServiceName() { } // Available only if you specify '-fe cxf' option in wsdl2java public ServiceName(Bus bus) { } @WebEndpoint(name="...") public SEI getPortName() { } . . . }
例 28.2 “生成的 Service Class 的概述” 中的 ServiceName
类定义以下方法:
-
service
Name (URL wsdlLocation, QName serviceName)
- 根据 wsdl:service 元素中的带有 QName ServiceName 服务的wsdl:service
元素中的数据构建服务对象,该对象可从 wsdlLocation 获取的 WSDL 合同中。 -
servicename()
- 默认构造器。它根据服务名称和在生成 stub 代码时提供的 WSDL 合同构建服务对象(例如,在运行wsdl2java
工具时)。使用此构造器假定 WSDL 合同在指定位置仍然可用。 -
serviceName(Bus bus)
- (CXF specific) 一个额外的构造器,允许您指定用于配置该服务的 Bus 实例。这在多线程应用程序上下文中很有用,其中多个总线实例可以与不同的线程关联。此构造器提供了一种简单的方法,可确保您指定的总线是此服务一起使用的总线。仅在调用wsdl2java
工具时指定-fe cxf
选项时才可用。 -
getPortName()
- 为wsdl:port
元素定义的端点返回代理,其name
属性等于 PortName。为 ServiceName 服务定义的每个wsdl:port
元素生成 getter 方法。wsdl:service
元素包含多个端点定义,生成带有多个getPortName()方法的
服务类。
服务端点接口
对于原始 WSDL 合同中定义的每个接口,您可以生成对应的 SEI。服务端点接口是 wsdl:portType
元素的 Java 映射。原始 wsdl:portType
元素中定义的每个操作都映射到 SEI 中的对应方法。操作的参数映射如下: .输入参数映射到 method 参数。
- 第一个输出参数映射到一个返回值。
-
如果有多个输出参数,第二个和后续输出参数映射到方法参数(更多,必须使用
Holder
类型传递这些参数的值)。
例如,例 28.3 “Greeter 服务端点接口” 显示 Greeter SEI,它从 例 26.1 “helloworld WSDL Contract” 中定义的 wsdl:portType
元素生成。为简单起见,例 28.3 “Greeter 服务端点接口” 省略标准 JAXB 和 JAX-WS 注释。
例 28.3. Greeter 服务端点接口
package org.apache.hello_world_soap_http; ... public interface Greeter { public String sayHi(); public String greetMe(String requestType); public void greetMeOneWay(String requestType); public void pingMe() throws PingMeFault; }
消费者主要功能
例 28.4 “消费者实施代码” 显示实施 HelloWorld 使用者的代码。消费者连接到 SOAPService 服务上的 SoapPort 端口,然后继续调用 Greeter 端口类型支持的每个操作。
例 28.4. 消费者实施代码
package demo.hw.client; import java.io.File; import java.net.URL; import javax.xml.namespace.QName; import org.apache.hello_world_soap_http.Greeter; import org.apache.hello_world_soap_http.PingMeFault; import org.apache.hello_world_soap_http.SOAPService; public final class Client { private static final QName SERVICE_NAME = new QName("http://apache.org/hello_world_soap_http", "SOAPService"); private Client() { } public static void main(String args[]) throws Exception { if (args.length == 0) { System.out.println("please specify wsdl"); System.exit(1); } URL wsdlURL; File wsdlFile = new File(args[0]); if (wsdlFile.exists()) { wsdlURL = wsdlFile.toURL(); } else { wsdlURL = new URL(args[0]); } System.out.println(wsdlURL); SOAPService ss = new SOAPService(wsdlURL,SERVICE_NAME); Greeter port = ss.getSoapPort(); String resp; System.out.println("Invoking sayHi..."); resp = port.sayHi(); System.out.println("Server responded with: " + resp); System.out.println(); System.out.println("Invoking greetMe..."); resp = port.greetMe(System.getProperty("user.name")); System.out.println("Server responded with: " + resp); System.out.println(); System.out.println("Invoking greetMeOneWay..."); port.greetMeOneWay(System.getProperty("user.name")); System.out.println("No response from server as method is OneWay"); System.out.println(); try { System.out.println("Invoking pingMe, expecting exception..."); port.pingMe(); } catch (PingMeFault ex) { System.out.println("Expected exception: PingMeFault has occurred."); System.out.println(ex.toString()); } System.exit(0); } }
例 28.4 “消费者实施代码” 中的 Client.main ()
方法按如下方式进行:
只要 Apache CXF 运行时类位于您的 classpath 上,则会隐式初始化运行时。不需要调用特殊功能来初始化 Apache CXF。
消费者需要一个字符串参数,它为 HelloWorld 提供 WSDL 合同的位置。WSDL 合同的位置存储在 wsdlURL
中。
您可以使用构造器创建服务对象,它需要 WSDL 合同的位置和服务名称。 调用适当的 getPortName()
方法以获取所需端口的实例。在这种情况下,SOAPService 服务只支持 SoapPort 端口,它实现了 Greeter
服务端点接口。
消费者调用 Greeter 服务端点接口支持的每个方法。
对于 pingMe ()
方法,示例代码演示了如何捕获 PingMeFault 错误异常。
使用 -fe cxf 选项生成的客户端代理
如果您在 wsdl2java (选择 cxf
frontend)中指定 -fe cxf
选项来生成客户端代理,则生成的客户端代理代码最好与 Java 7 集成。在这种情况下,当调用 getServiceNamePort ()
方法时,您可以返回一个类型,它是 SEI 的子接口,并实现以下附加接口:
-
java.lang.AutoCloseable
-
javax.xml.ws.BindingProvider
(JAX-WS 2.0) -
org.apache.cxf.endpoint.Client
要查看这如何简化使用客户端代理,请考虑以下 Java 代码示例,使用标准 JAX-WS 代理对象编写:
// Programming with standard JAX-WS proxy object // (ServiceNamePortType port = service.getServiceNamePort(); ((BindingProvider)port).getRequestContext() .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); port.serviceMethod(...); ((Closeable)port).close();
将前面的代码与以下等效代码示例进行比较,使用 cxf
frontend 生成的代码编写:
// Programming with proxy generated using '-fe cxf' option // try (ServiceNamePortTypeProxy port = service.getServiceNamePort()) { port.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); port.serviceMethod(...); }