Spring-WS 提供了一个客户端 Web 服务 API,允许以一致的、XML 驱动的方式访问 Web 服务。它还支持使用编组器(marshaller)和解组器(unmarshaller),以便您的服务层代码可以完全处理 Java 对象。
org.springframework.ws.client.core 包提供了使用客户端访问 API 的核心功能。它包含简化 Web 服务使用的模板类,就像 Spring 核心的 JdbcTemplate 对 JDBC 所做的那样。Spring 模板类通用的设计原则是提供辅助方法来执行常见操作,对于更复杂的用法,则委托给用户实现的 callback 接口。Web 服务模板遵循相同的设计。这些类提供了各种便利方法,用于发送和接收 XML 消息,在发送前将对象编组为 XML,并允许多种传输选项。
WebServiceTemplate 是 Spring-WS 中用于客户端 Web 服务访问的核心类。它包含发送 Source 对象的方法,以及将响应消息作为 Source 或 Result 接收的方法。此外,它还可以在通过传输发送对象之前将其编组为 XML,并将任何响应 XML 解组回对象。
WebServiceTemplate 类使用 URI 作为消息目的地。您可以在模板本身上设置 defaultUri 属性,或者在调用模板上的方法时显式提供 URI。URI 将解析为 WebServiceMessageSender,后者负责通过传输层发送 XML 消息。您可以使用 WebServiceTemplate 类的 messageSender 或 messageSenders 属性设置一个或多个消息发送器。
有两种 WebServiceMessageSender 接口的实现用于通过 HTTP 发送消息。默认实现是 HttpUrlConnectionMessageSender,它使用 Java 自身提供的功能。另一种选择是 HttpComponentsMessageSender,它使用 Apache HttpComponents HttpClient。如果您需要更高级且易于使用的功能(例如身份验证、HTTP 连接池等),请使用后者。
要使用 HTTP 传输,请将 defaultUri 设置为类似 http://example.com/services 的内容,或者为其中一个方法提供 uri 参数。
以下示例展示了如何将默认配置用于 HTTP 传输
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
</beans>
以下示例展示了如何覆盖默认配置,并使用 Apache HttpClient 进行 HTTP 身份验证
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="credentials">
<bean class="org.apache.http.auth.UsernamePasswordCredentials">
<constructor-arg value="john:secret"/>
</bean>
</property>
</bean>
</property>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
为了通过 JMS 发送消息,Spring Web Services 提供了 JmsMessageSender。此类使用 Spring 框架的功能将 WebServiceMessage 转换为 JMS Message,在 Queue 或 Topic 上发送它,并接收响应(如果有)。
要使用 JmsMessageSender,您需要将 defaultUri 或 uri 参数设置为 JMS URI,该 URI 至少包含 jms: 前缀和目标名称。一些 JMS URI 示例包括:jms:SomeQueue、jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT 和 jms:RequestQueue?replyToName=ResponseName。有关此 URI 语法的更多信息,请参阅 JmsMessageSender 的类级别 Javadoc。
默认情况下,JmsMessageSender 发送 JMS BytesMessage,但可以通过在 JMS URI 上使用 messageType 参数来覆盖此设置以使用 TextMessages。例如:jms:Queue?messageType=TEXT_MESSAGE。请注意,BytesMessages 是首选类型,因为 TextMessages 不可靠地支持附件和字符编码。
以下示例展示了如何将 JMS 传输与 ActiceMQ 连接工厂结合使用
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://?broker.persistent=false"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
</property>
<property name="defaultUri" value="jms:RequestQueue?deliveryMode=NON_PERSISTENT"/>
</bean>
</beans>
Spring Web Services 还提供了一种电子邮件传输方式,可用于通过 SMTP 发送 Web 服务消息,并通过 POP3 或 IMAP 检索它们。客户端电子邮件功能包含在 MailMessageSender 类中。此类从请求 WebServiceMessage 创建电子邮件消息,并通过 SMTP 发送。然后,它等待响应消息到达传入的 POP3 或 IMAP 服务器。
要使用 MailMessageSender,请将 defaultUri 或 uri 参数设置为 mailto URI。以下是一些 URI 示例:mailto:[email protected] 和 mailto:server@localhost?subject=SOAP%20Test。确保消息发送器已正确配置 transportUri(指示用于发送请求的服务器,通常是 SMTP 服务器)和 storeUri(指示用于轮询响应的服务器,通常是 POP3 或 IMAP 服务器)。
以下示例展示了如何使用电子邮件传输
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.mail.MailMessageSender">
<property name="from" value="Spring-WS SOAP Client <[email protected]>"/>
<property name="transportUri" value="smtp://client:[email protected]"/>
<property name="storeUri" value="imap://client:[email protected]/INBOX"/>
</bean>
</property>
<property name="defaultUri" value="mailto:[email protected]?subject=SOAP%20Test"/>
</bean>
</beans>
Spring Web Services 2.0 引入了 XMPP (Jabber) 传输,可用于通过 XMPP 发送和接收 Web 服务消息。客户端 XMPP 功能包含在 XmppMessageSender 类中。此类从请求 WebServiceMessage 创建 XMPP 消息,并通过 XMPP 发送。然后它会监听响应消息的到来。
要使用 XmppMessageSender,请将 defaultUri 或 uri 参数设置为 xmpp URI,例如 xmpp:[email protected]。发送方还需要一个 XMPPConnection 才能工作,该连接可以使用 org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean 方便地创建。
以下示例展示了如何使用 xmpp 传输
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.xmpp.XmppMessageSender">
<property name="connection" ref="connection"/>
</bean>
</property>
<property name="defaultUri" value="xmpp:[email protected]"/>
</bean>
</beans>
WebServiceTemplate 包含许多方便的方法来发送和接收 Web 服务消息。有些方法接受并返回 Source,有些方法返回 Result。此外,还有将对象编组和解组为 XML 的方法。这是一个将简单 XML 消息发送到 Web 服务的示例。
import java.io.StringReader;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.WebServiceMessageSender;
public class WebServiceClient {
private static final String MESSAGE =
"<message xmlns=\"http://tempuri.org\">Hello Web Service World</message>";
private final WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
public void setDefaultUri(String defaultUri) {
webServiceTemplate.setDefaultUri(defaultUri);
}
// send to the configured default URI
public void simpleSendAndReceive() {
StreamSource source = new StreamSource(new StringReader(MESSAGE));
StreamResult result = new StreamResult(System.out);
webServiceTemplate.sendSourceAndReceiveToResult(source, result);
}
// send to an explicit URI
public void customSendAndReceive() {
StreamSource source = new StreamSource(new StringReader(MESSAGE));
StreamResult result = new StreamResult(System.out);
webServiceTemplate.sendSourceAndReceiveToResult("https://:8080/AnotherWebService",
source, result);
}
}
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="webServiceClient" class="WebServiceClient">
<property name="defaultUri" value="https://:8080/WebService"/>
</bean>
</beans>
上述示例使用 WebServiceTemplate 将 hello world 消息发送到位于 https://:8080/WebService 的 Web 服务(对于 simpleSendAndReceive() 方法),并将结果写入控制台。WebServiceTemplate 注入了默认 URI,因为 Java 代码中没有显式提供 URI,所以使用了默认 URI。
请注意,WebServiceTemplate 类一旦配置(假设其所有依赖项也是线程安全的,Spring-WS 附带的所有依赖项都是如此),就是线程安全的,因此如果需要,多个对象可以使用相同的共享 WebServiceTemplate 实例。WebServiceTemplate 公开了一个零参数构造函数和 messageFactory/messageSender bean 属性,可用于构造实例(使用 Spring 容器或纯 Java 代码)。或者,考虑从 Spring-WS 的 WebServiceGatewaySupport 方便基类派生,该基类公开了方便的 bean 属性以实现轻松配置。(您不必扩展此基类...它仅作为便利类提供。)
为了方便发送普通 Java 对象,WebServiceTemplate 有许多 send(..) 方法,它们以 Object 作为消息数据内容的参数。WebServiceTemplate 类中的 marshalSendAndReceive(..) 方法将请求对象转换为 XML 的工作委托给 Marshaller,并将响应 XML 转换为对象的工作委托给 Unmarshaller。(有关编组器和解组器的更多信息,请参阅 Spring 文档。)通过使用编组器,您的应用程序代码可以专注于正在发送或接收的业务对象,而不必担心它如何表示为 XML 的细节。为了使用编组功能,您必须使用 WebServiceTemplate 类的 marshaller/unmarshaller 属性设置编组器和解组器。
为了适应 SOAP 头部和其他消息设置,WebServiceMessageCallback 接口允许您在消息创建之后但在发送之前访问消息。以下示例演示如何设置通过编组对象创建的消息上的 SOAP Action 头部。
public void marshalWithSoapActionHeader(MyObject o) {
webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
((SoapMessage)message).setSoapAction("http://tempuri.org/Action");
}
});
}
请注意,您还可以使用 org.springframework.ws.soap.client.core.SoapActionCallback 来设置 SOAP Action 头部。
除了服务器端 WS-Addressing 支持之外,Spring Web Services 在客户端也支持此规范。
要在客户端设置 WS-Addressing 头部,您可以使用 org.springframework.ws.soap.addressing.client.ActionCallback。此回调将所需的 Action 头部作为参数。它还有用于指定 WS-Addressing 版本和 To 头部 的构造函数。如果未指定,To 头部将默认为正在建立的连接的 URL。
以下是设置 Action 头部为 http://samples/RequestOrder 的示例
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
WebServiceMessageExtractor 接口是一个低级回调接口,允许您完全控制从接收到的 WebServiceMessage 中提取 Object 的过程。WebServiceTemplate 将在提供给它的 WebServiceMessageExtractor 上调用 extractData(..) 方法,同时与服务资源的底层连接仍然打开。以下示例说明了 WebServiceMessageExtractor 的实际应用
public void marshalWithSoapActionHeader(final Source s) {
final Transformer transformer = transformerFactory.newTransformer();
webServiceTemplate.sendAndReceive(new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
transformer.transform(s, message.getPayloadResult());
},
new WebServiceMessageExtractor() {
public Object extractData(WebServiceMessage message) throws IOException
// do your own transforms with message.getPayloadResult()
// or message.getPayloadSource()
}
});
}
在测试您的 Web 服务客户端(即使用 WebServiceTemplate 访问 Web 服务的类)时,有两种可能的方法
编写单元测试,它只是模拟 WebServiceTemplate 类、WebServiceOperations 接口或完整的客户端类。
这种方法的优点是很容易实现;缺点是您并没有真正测试通过网络发送的 XML 消息的确切内容,尤其是在模拟整个客户端类时。
编写集成测试,它确实测试消息的内容。
第一种方法可以很容易地通过 EasyMock、JMock 等模拟框架实现。下一节将重点介绍如何使用 Spring Web Services 2.0 中引入的测试功能编写集成测试。
Spring Web Services 2.0 引入了对创建 Web 服务客户端集成测试的支持。在此上下文中,客户端是使用 WebServiceTemplate 访问 Web 服务的类。
集成测试支持位于 org.springframework.ws.test.client 包中。该包中的核心类是 MockWebServiceServer。其基本思想是 Web 服务模板连接到这个模拟服务器,向其发送请求消息,然后模拟服务器根据注册的期望进行验证。如果满足期望,模拟服务器然后准备一个响应消息,并将其发送回模板。
MockWebServiceServer 的典型用法是
通过调用 MockWebServiceServer.createServer(WebServiceTemplate)、MockWebServiceServer.createServer(WebServiceGatewaySupport) 或 MockWebServiceServer.createServer(ApplicationContext) 创建 MockWebServiceServer 实例。
通过调用 expect(RequestMatcher) 来设置请求期望,可能使用 RequestMatchers 中提供的默认 RequestMatcher 实现(可以静态导入)。可以通过链式调用 andExpect(RequestMatcher) 来设置多个期望。
通过调用 andRespond(ResponseCreator) 来创建适当的响应消息,可能使用 ResponseCreators 中提供的默认 ResponseCreator 实现(可以静态导入)。
正常使用 WebServiceTemplate,无论是直接使用还是通过客户端代码使用。
调用 MockWebServiceServer.verify() 以确保所有期望都已满足。
请注意,MockWebServiceServer(及相关类)提供了一个“流畅”的 API,因此您通常可以使用 IDE 中的代码完成功能(即 ctrl-space)来指导您完成设置模拟服务器的过程。
另请注意,您在单元测试中依赖于 Spring Web Services 中提供的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关更多信息,请参阅第 4.4 节,“消息日志记录和跟踪”。
例如,考虑这个 Web 服务客户端类
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
public class CustomerClient extends WebServiceGatewaySupport {
public int getCustomerCount() {
CustomerCountRequest request = new CustomerCountRequest();
request.setCustomerName("John Doe");
CustomerCountResponse response =
(CustomerCountResponse) getWebServiceTemplate().marshalSendAndReceive(request);
return response.getCustomerCount();
}
}
|
|
|
|
|
|
CustomerClient 的典型测试如下所示
import javax.xml.transform.Source; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.xml.transform.StringSource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; import org.springframework.ws.test.client.MockWebServiceServer;import static org.springframework.ws.test.client.RequestMatchers.*;
import static org.springframework.ws.test.client.ResponseCreators.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("integration-test.xml")
public class CustomerClientIntegrationTest { @Autowired private CustomerClient client;
private MockWebServiceServer mockServer;
@Before public void createServer() throws Exception { mockServer = MockWebServiceServer.createServer(client); } @Test public void customerClient() throws Exception { Source requestPayload = new StringSource( "<customerCountRequest xmlns='http://springframework.org/spring-ws'>" + "<customerName>John Doe</customerName>" + "</customerCountRequest>"); Source responsePayload = new StringSource( "<customerCountResponse xmlns='http://springframework.org/spring-ws'>" + "<customerCount>10</customerCount>" + "</customerCountResponse>"); mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));
int result = client.getCustomerCount();
assertEquals(10, result);
mockServer.verify();
} }
|
|
|
此测试使用 Spring 框架中提供的标准测试工具。这不是必需的,但通常是设置测试的最简单方法。 |
|
|
|
在 |
|
我们通过调用 我们还通过调用 测试的这部分可能看起来有点令人困惑,但 IDE 的代码完成功能非常有用。在输入 |
|
我们调用 |
|
我们调用 |
为了验证请求消息是否符合某些期望,MockWebServiceServer 使用 RequestMatcher 策略接口。此接口定义的契约非常简单
public interface RequestMatcher {
void match(URI uri,
WebServiceMessage request)
throws IOException,
AssertionError;
}
您可以编写自己的此接口实现,在消息不符合您的期望时抛出 AssertionError,但您当然不必这样做。RequestMatchers 类为您提供了标准 RequestMatcher 实现,供您在测试中使用。您通常会静态导入此类。
RequestMatchers 类提供以下请求匹配器
RequestMatchers 方法 | 描述 |
|---|---|
anything() | 期望任何类型的请求。 |
payload() | 期望给定的请求负载。 |
validPayload() | 期望请求负载根据给定的 XSD 模式进行验证。 |
xpath() | 期望给定的 XPath 表达式存在,不存在,或评估为给定值。 |
soapHeader() | 期望请求消息中存在给定的 SOAP 头部。 |
connectionTo() | 期望连接到给定的 URL。 |
您可以通过链式调用 andExpect() 来设置多个请求期望,如下所示
mockServer.expect(connectionTo("http://example.com")).
andExpect(payload(expectedRequestPayload)).
andExpect(validPayload(schemaResource)).
andRespond(...);
有关 RequestMatchers 提供的请求匹配器的更多信息,请参阅类级别 Javadoc。
当请求消息经过验证并满足定义的期望时,MockWebServiceServer 将为 WebServiceTemplate 创建一个响应消息以供使用。服务器为此目的使用 ResponseCreator 策略接口
public interface ResponseCreator {
WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory messageFactory)
throws IOException;
}
您同样可以编写自己的此接口实现,使用消息工厂创建响应消息,但您当然不必这样做,因为 ResponseCreators 类为您提供了标准 ResponseCreator 实现,供您在测试中使用。您通常会静态导入此类。
ResponseCreators 类提供以下响应
ResponseCreators 方法 | 描述 |
|---|---|
withPayload() | 创建具有给定负载的响应消息。 |
withError() | 在响应连接中创建错误。此方法让您有机会测试错误处理。 |
withException() | 从响应连接读取时抛出异常。此方法让您有机会测试异常处理。 |
withMustUnderstandFault()、withClientOrSenderFault()、withServerOrReceiverFault() 和 withVersionMismatchFault() | 创建具有给定 SOAP 错误的响应消息。此方法让您有机会测试故障处理。 |
有关 RequestMatchers 提供的请求匹配器的更多信息,请参阅类级别 Javadoc。