通过java提供的“wsimport”工具可以很容易完成基于SOAP协议的web服务客户端生成工作。这个工具可以从对应的WSDL文档的服务描述中生成客户端支持代码或其它相关资源。输入并执行如下命令:
1
|
% wsimport
|
执行后,将会打印出该命令程序的使用帮助。下面我们将通过命令工具对前面的例子TimeServer服务生成客户端开发相关支持代码。启动ch01.ts.TimeServerPublisher程序后,执行如下命令:
1
|
% wsimport -keep -p client http:
//localhost
:9876
/ts
?wsdl
|
执行后将会在“client”子目录下面产生两个源代码文件和两个与之对应的已经编译好的Class文件。“-p”选项用来指定生成的java包名称,此处使用“client”作为包名称。包名称可以是符合java包命名规则的任何名称,wsimport工具通过指定的包名称创建包对应子目录。“-keep”选项用来指示是否保留编译后的源文件,这个例子中,我们保留源文件留作验证。“-p”选项是比较重要的,这是由于wsimport命令产生的TimeServer.class文件名称同先前编译的服务端点接口(SEI)拥有同样的名字。如果没有指定包名称,wsimport默认使用服务器实现的包名称作为客户端代码包名称,此处将默认用“ch01.ts”作为包名称。简单来说,使用“-p”选项可以防止编译好的SEI文件不被wsimport工具产生的文件覆盖。如果已经将WSDL文档保存在本地(比如,文件被命名为ts.wsdl),那么,wsimport可以写成:
1
|
% wsimport -keep -p client ts.wsdl
|
生成的代码示例如下,由wsimport生成的TimeServer服务接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package
client;
import
javax.jws.WebMethod;
import
javax.jws.WebResult;
import
javax.jws.WebService;
import
javax.jws.soap.SOAPBinding;
import
javax.xml.ws.Action;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.4-b01
* Generated source version: 2.2
*/
@WebService
(name =
"TimeServer"
, targetNamespace =
"http://ts.ch01/"
)
@SOAPBinding
(style = SOAPBinding.Style.RPC)
public
interface
TimeServer {
/**
* @return
* returns java.lang.String
*/
@WebMethod
@WebResult
(partName =
"return"
)
@Action
(input =
"http://ts.ch01/TimeServer/getTimeAsStringRequest"
,
output =
"http://ts.ch01/TimeServer/getTimeAsStringResponse"
)
public
String getTimeAsString();
/**
* @return
* returns long
*/
@WebMethod
@WebResult
(partName =
"return"
)
@Action
(input =
"http://ts.ch01/TimeServer/getTimeAsElasedRequest"
,
output =
"http://ts.ch01/TimeServer/getTimeAsElasedResponse"
)
public
long
getTimeAsElased();
}
|
由wsimport生成的TimeServerImplService实现类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package
client;
import
java.net.MalformedURLException;
import
java.net.URL;
import
javax.xml.namespace.QName;
import
javax.xml.ws.Service;
import
javax.xml.ws.WebEndpoint;
import
javax.xml.ws.WebServiceClient;
import
javax.xml.ws.WebServiceException;
import
javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.4-b01
* Generated source version: 2.2
*/
@WebServiceClient
(name =
"TimeServerImplService"
, targetNamespace =
"http://ts.ch01/"
,
wsdlLocation =
"http://localhost:9876/ts?wsdl"
)
public
class
TimeServerImplService
extends
Service{
private
final
static
URL TIMESERVERIMPLSERVICE_WSDL_LOCATION;
private
final
static
WebServiceException TIMESERVERIMPLSERVICE_EXCEPTION;
private
final
static
QName TIMESERVERIMPLSERVICE_QNAME =
new
QName(
"http://ts.ch01/"
,
"TimeServerImplService"
);
static
{
URL url =
null
;
WebServiceException e =
null
;
try
{
url =
new
URL(
"http://localhost:9876/ts?wsdl"
);
}
catch
(MalformedURLException ex) {
e =
new
WebServiceException(ex);
}
TIMESERVERIMPLSERVICE_WSDL_LOCATION = url;
TIMESERVERIMPLSERVICE_EXCEPTION = e;
}
public
TimeServerImplService() {
super
(__getWsdlLocation(), TIMESERVERIMPLSERVICE_QNAME);
}
public
TimeServerImplService(WebServiceFeature... features) {
super
(__getWsdlLocation(), TIMESERVERIMPLSERVICE_QNAME, features);
}
public
TimeServerImplService(URL wsdlLocation) {
super
(wsdlLocation, TIMESERVERIMPLSERVICE_QNAME);
}
public
TimeServerImplService(URL wsdlLocation, WebServiceFeature... features) {
super
(wsdlLocation, TIMESERVERIMPLSERVICE_QNAME, features);
}
public
TimeServerImplService(URL wsdlLocation, QName serviceName) {
super
(wsdlLocation, serviceName);
}
public
TimeServerImplService(URL wsdlLocation, QName serviceName,
WebServiceFeature... features) {
super
(wsdlLocation, serviceName, features);
}
/**
* @return
* returns TimeServer
*/
@WebEndpoint
(name =
"TimeServerImplPort"
)
public
TimeServer getTimeServerImplPort() {
return
super
.getPort(
new
QName(
"http://ts.ch01/"
,
"TimeServerImplPort"
),
TimeServer.
class
);
}
/**
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.
* Supported features not in the <code>features</code> parameter will have
* their default values.
* @return
* returns TimeServer
*/
@WebEndpoint
(name =
"TimeServerImplPort"
)
public
TimeServer getTimeServerImplPort(WebServiceFeature... features) {
return
super
.getPort(
new
QName(
"http://ts.ch01/"
,
"TimeServerImplPort"
),
TimeServer.
class
, features);
}
private
static
URL __getWsdlLocation() {
if
(TIMESERVERIMPLSERVICE_EXCEPTION!=
null
) {
throw
TIMESERVERIMPLSERVICE_EXCEPTION;
}
return
TIMESERVERIMPLSERVICE_WSDL_LOCATION;
}
}
|
由wsimport产生的源文件有3点需要注意。第一,客户端接口client.TimeServer定义了同目标SEI接口TimeServer相同的方法。这些方法包括服务请求操作getTimeAsString和getTimeAsElapsed两个方法。第二,实现类client.TimeServerImplService包括一个无参构造函数,同目标Java客户端TimeClient中构造服务对象时非常地类似。第三,TimeServerImplService类封装了getTimeServerImplPort方法,该方法返回一个client.TimeServer对象实例,该对象实例支持对两个预先定义的web服务操作的调用。
下面利用wsimport工具产生的客户端支持代码来调用web服务,如例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package
client;
/**
* 使用wsimport生成的代码编写的java服务调用客户端
* @author fuhd
*/
public
class
TimeClientWSDL {
public
static
void
main(String[] args) {
TimeServerImplService service =
new
TimeServerImplService();
TimeServer server = service.getTimeServerImplPort();
System.out.println(server.getTimeAsElased());
System.out.println(server.getTimeAsString());
}
}
|
上面示例中的服务调用客户端和前面的TimeClient客户端在功能上是一样的,但是这个客户端编写起来却非常容易。尤为重要的是,在使用wsimport工具生成client.TimeServerImplService时,像与服务对应的QName和服务端点等这些复杂的细节都被隐藏掉了。
下面是针对采用基于WSDL协议所生成的相关工件(Artifact)编写服务客户端的一些常用做法,比如像这里的TimeServer和TimeServerImplService。
-
首先,通过wsimport产生的类中的几个构造方法构造服务对象,比如在本例的client.TimeServerImplService类中,无参数的构造方法更可取,因为这样更为简单。然而,在本例中,还是生成了几个拥有多个输入参数的构造方法,用来适应web服务的命名空间(URI)或服务端点(RUL)等变化。当然还可以通过wsimport工具包重新生成基于WSDL的java文件,用在其他客户端开发中。
-
通过构建的服务对象调用对应的get系列方法,在本例中是getTimeServerImplPort方法。这个方法返回一个封装了web服务操作方法的对象,本例中分别是定义在目标SEI中的getTimeAsString和getTimeAsElapsed方法。