jax-ws 生成soap
Web服务的基础之一是互操作性。 这意味着Web服务以标准格式发送和接收消息。 通常,该格式是SOAP。 有很多发送SOAP消息的方法。
最基本的是对URL流执行println
,但这要求您对SOAP协议以及服务期望并发送的SOAP消息了解得太多。 没有简单的方法可以从服务中获取此信息,并且这种方法的可伸缩性不是很高。
加强使用SAAJ API(带有Java附件的SOAP)。 SAAJ API为您提供了一种在更抽象的级别上操作SOAP构造的方法,但是它们仍然与URL流共享许多println
问题。
一种更友好的方法是使用一种将应用程序拉到SOAP消息传递协议之上的抽象级别的系统。 这种系统的好处包括:
- 应用程序程序员可以专注于他们的应用程序逻辑,而不是SOAP的严格性
- 可以使用除SOAP之外的其他消息传递方案,并且即使有的话,应用程序代码的更改也很少。
基于XML的RPC的Java API(JAX-RPC)提供了这种抽象方法。
JAX-RPC和Barnes&Noble Web服务
JAX-RPC不再依赖SOAP,而是依赖于Web服务描述语言(WSDL)。 WSDL以声明性,标准化的方式为Web服务定义API。 WSDL可以为服务定义SOAP绑定,但是它也可以在SOAP消息传递层之上的级别上定义服务的更抽象描述。 (请参阅相关主题有关WSDL的更多细节。)
上面提到的SAAJ文章中的示例使用了Barnes&Noble Web服务。 XMethods站点还提供了此Web服务的WSDL(请参阅参考资料 )。 在这些示例中使用了WSDL -请参见清单1。
清单1. XMethods的Barnes&Noble WSDL
<?xml version="1.0" ?>
<definitions name="BNQuoteService"
targetNamespace="http://www.xmethods.net/sd/BNQuoteService.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://www.xmethods.net/sd/BNQuoteService.wsdl">
<message name="getPriceRequest">
<part name="isbn" type="xsd:string" />
</message>
<message name="getPriceResponse">
<part name="return" type="xsd:float" />
</message>
<portType name="BNQuotePortType">
<operation name="getPrice">
<input message="tns:getPriceRequest" name="getPrice" />
<output message="tns:getPriceResponse" name="getPriceResponse" />
</operation>
</portType>
<binding name="BNQuoteBinding" type="tns:BNQuotePortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="getPrice">
<soap:operation soapAction="" />
<input name="getPrice">
<soap:body
use="encoded"
namespace="urn:xmethods-BNPriceCheck"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</input>
<output name="getPriceResponse">
<soap:body
use="encoded"
namespace="urn:xmethods-BNPriceCheck"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</output>
</operation>
</binding>
<service name="BNQuoteService">
<documentation>Returns price of a book at BN.com given an
ISBN number</documentation>
<port name="BNQuotePort" binding="tns:BNQuoteBinding">
<soap:address location=
"http://services.xmethods.net:80/soap/servlet/rpcrouter" />
</port>
</service>
</definitions>
在任何JAX-RPC应用程序中,您必须做三件事:
- 实例化
Service
类,它是Web服务的客户端表示形式 - 实例化Web服务应用程序的代理(并可能设置该代理)
- 调用Web服务的应用程序的操作
JAX-RPC DII客户端应用程序
SAAJ API不依赖于WSDL,仅依赖于SOAP,但是您必须确切地知道该服务的SOAP消息是什么样的。 JAX-RPC定义了一个动态调用接口(DII)API来访问Web服务,严格来说,该服务也不依赖WSDL(尽管您必须了解WSDL中捕获的某些信息)。 但是,您不再需要了解SOAP消息的详细信息。 DII JAX-RPC应用程序特定的三个步骤是:
- 实例化没有WSDL的DII
Service
类 - 实例化DII
Call
对象代理并进行设置 - 调用
Call
对象的invoke
方法
实例化没有WSDL的DII Service
类
此类不知道WSDL,但必须有一个名称。 使用WSDL中的服务名称。
清单2.实例化一个DII服务类
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
实例化DII Call
对象代理并进行设置
DII代理对象实现javax.xml.rpc.Call
接口。 您可以从刚刚创建的服务中获得Call
接口的实现。
清单3.实例化一个Call对象
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
// Now create a dynamic Call object from this service.
// This call object is not yet associated with any
// operation. We'll do that below.
Call call = service.createCall();
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
您现在拥有的Call
对象尚未可用。 它对操作一无所知。 向对象提供以下数据:
- 操作名称:
getPrice
- 输入参数:字符串
- 返回类型:浮点数
- 绑定信息:rpc样式; 编码风格
- 端点:
http://services.xmethods.net:80/soap/servlet/rpcrouter
://services.xmethods.net:http://services.xmethods.net:80/soap/servlet/rpcrouter
soap / servlet /http://services.xmethods.net:80/soap/servlet/rpcrouter
清单4.填充Call对象
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
// Now create a dynamic Call object from this service.
// This call object is not yet associated with any
// operation. We'll do that below.
Call call = service.createCall();
// Next, build up the information about the operation...
// The operation name
QName operationName = new QName(
"urn:xmethods-BNPriceCheck",
"getPrice");
call.setOperationName(operationName);
// The input parameter
call.addParameter(
"isbn", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode
// The return
call.setReturnType(XMLType.XSD_FLOAT);
// The operation is an RPC-style operation.
call.setProperty(
Call.OPERATION_STYLE_PROPERTY,
"rpc");
// The encoding style property value comes from the
// binding's operation's input clauses encodingStyle
// attribute. Note that, in this case, this call is not
// really necessary - the value we're setting this
// property to is the default.
call.setProperty(
Call.ENCODINGSTYLE_URI_PROPERTY,
"http://schemas.xmlsoap.org/soap/encoding/");
// The target endpoint
call.setTargetEndpointAddress(
"http://services.xmethods.net:80/soap/servlet/rpcrouter");
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
调用Call
对象的invoke方法
最后,您现在可以调用getPrice
操作了。
清单5.调用getPrice操作
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;
public class DIITip {
public static void main(String args[]) {
try {
// Create a service class with no WSDL information. You
// still must know something about the WSDL, however: the
// service's name.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(serviceName);
// Now create a dynamic Call object from this service.
// This call object is not yet associated with any
// operation. We'll do that below.
Call call = service.createCall();
// Next, build up the information about the operation...
// The operation name
QName operationName = new QName(
"urn:xmethods-BNPriceCheck",
"getPrice");
call.setOperationName(operationName);
// The input parameter
call.addParameter(
"isbn", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode
// The return
call.setReturnType(XMLType.XSD_FLOAT);
// The operation is an RPC-style operation.
call.setProperty(
Call.OPERATION_STYLE_PROPERTY,
"rpc");
// The encoding style property value comes from the
// binding's operation's input clauses encodingStyle
// attribute. Note that, in this case, this call is not
// really necessary - the value we're setting this
// property to is the default.
call.setProperty(
Call.ENCODINGSTYLE_URI_PROPERTY,
"http://schemas.xmlsoap.org/soap/encoding/");
// The target endpoint
call.setTargetEndpointAddress(
"http://services.xmethods.net:80/soap/servlet/rpcrouter");
// Invoke the operation
Object[] actualArgs = {"0672324229"};
Float price = (Float) call.invoke(actualArgs);
System.out.println("price = " + price);
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
当您运行java DIITip
,您会得到价格: 44.99
。
如果将此与SAAJ文章中的代码进行比较,您会发现您与SOAP协议的知识相距很远。 您需要的唯一与SOAP相关的信息是操作样式和编码样式。
顾名思义,DII编程模型旨在与动态服务一起使用。 如果服务稍有变化,则不需要花费很多时间来更改客户端代码以使其匹配,并且如果您确实很聪明,则客户端代码可以是动态的,因此根本不需要更改。
该模型仍然相当复杂,尤其是在必须填充Call
对象的地方。 操作越复杂,需要提供给Call
对象的信息就越复杂。 因此,如果您知道服务是静态的并且不会改变,那么您可以在抽象食物链上再上一层。
JAX-RPC SEI客户端应用程序
下一个抽象级别需要更多的设置,但是生成的客户端代码要简单得多。 您从WSDL开始。 JAX-RPC实现提供了一个WSDL-to-Java代码生成器,该生成器除其他功能外还生成服务端点接口(SEI)。 这是Web服务应用程序的客户端Java接口。 清单6显示了XMethods Barnes&Noble应用程序的SEI。
清单6. Barnes&Noble SEI
package xmethods.bn;
public interface BNQuotePortType extends java.rmi.Remote {
public float getPrice(java.lang.String isbn)
throws java.rmi.RemoteException;
}
在我使用的WSDL-to-Java工具中,我将targetNamespace
映射到Java包xmethods.bn
,这就是生成SEI的包。 SEI的名称是从WSDL的portType
名称派生的: BNQuotePortType
。 getPrice
方法是从portType
的getPrice
操作及其引用的消息和类型派生的。 如果运行您喜欢的WSDL-to-Java工具,您将看到xmethods.bn
包中生成了其他类。 您必须全部编译它们,但是您的客户端应用程序只需要了解SEI。
要使用此SEI调用Web服务,请执行与DII基本上相同的三个步骤:
- 使用WSDL实例化
Service
类 - 实例化代理
- 调用代理的操作
SEI和DII的步骤1相似。 步骤2和3得以简化,尤其是步骤2。
使用WSDL实例化Service
类
SEI模型和DII模型中此步骤之间的唯一区别是,在这里您提供了额外的信息:WSDL。
清单7.实例化SEI的Service类
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class SEITip {
public static void main(String args[]) {
try {
// Create a service class with WSDL information.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
URL wsdlLocation = new URL
("http://www.xmethods.net/sd/2001/BNQuoteService.wsdl");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
wsdlLocation,
serviceName);
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
实例化代理
获得服务后,您必须找到SEI的实现。 此实现是真实应用程序的代理。 有关如何访问该应用程序的所有信息都隐藏在实现中-它是从WSDL的服务端口收集的-因此,一旦获得了代理,就无需进行任何设置。 已经为您完成了。
清单8.实例化SEI的实现
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import xmethods.bn.BNQuotePortType;
public class SEITip {
public static void main(String args[]) {
try {
// Create a service class with WSDL information.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
URL wsdlLocation = new URL
("http://www.xmethods.net/sd/2001/BNQuoteService.wsdl");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
wsdlLocation,
serviceName);
// Get an implementation for the SEI for the given port
QName portName = new QName("", "BNQuotePort");
BNQuotePortType quote = (BNQuotePortType) service.getPort(
portName,
BNQuotePortType.class);
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
调用代理的操作
最后,调用操作再简单不过了:
清单9.在SEI上调用一个操作
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import xmethods.bn.BNQuotePortType;
public class SEITip {
public static void main(String args[]) {
try {
// Create a service class with WSDL information.
QName serviceName = new QName(
"http://www.xmethods.net/sd/BNQuoteService.wsdl",
"BNQuoteService");
URL wsdlLocation = new URL
("http://www.xmethods.net/sd/2001/BNQuoteService.wsdl");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
wsdlLocation,
serviceName);
// Get an implementation for the SEI for the given port
QName portName = new QName("", "BNQuotePort");
BNQuotePortType quote = (BNQuotePortType) service.getPort(
portName,
BNQuotePortType.class);
// Invoke the operation
float price = quote.getPrice("0672324229");
System.out.println("price = " + price);
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
摘要
在调用Web服务的许多方法中,最基本的方法是手动生成和发送SOAP消息。 您必须对服务和SOAP协议了解太多,这不是很有用。 下一步是使用SAAJ API。 您仍然必须对服务和SOAP协议了解很多-仍然不是特别有用。 从那里开始的下一步是使用JAX-RPC DII。 这使您远离SOAP,但是代码仍然相当复杂。 通常,最好的方法是从服务的WSDL生成SEI并调用SEI代理。
翻译自: https://www.ibm.com/developerworks/xml/library/x-tipjaxrpc/index.html
jax-ws 生成soap