Spring-WS 提供了一个客户端 Web 服务 API,允许一致地通过 XML 访问 Web 服务。它也支持使用 marshaller 和 unmarshaller,这样你的服务层代码就可以完全处理 Java 对象。
org.springframework.ws.client.core 包提供了使用客户端访问 API 的核心功能。它包含简化 Web 服务使用的模板类,很像 Spring 核心的 JdbcTemplate 之于 JDBC。Spring 模板类共同的设计原则是提供辅助方法来执行常见操作,对于更复杂的使用,则委托给用户实现的 callback 接口。Web 服务模板遵循相同的设计。这些类提供了发送和接收 XML 消息、在发送前将对象 marshalling 到 XML,以及支持多种传输选项的各种便利方法。
WebServiceTemplate 是 Spring-WS 中客户端 Web 服务访问的核心类。它包含发送 Source 对象,以及接收响应消息作为 Source 或 Result 的方法。此外,它可以在通过传输发送对象之前将对象 marshalling 到 XML,并将任何响应 XML 再次 unmarshalling 为对象。
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 传输与 ActiveMQ 连接工厂结合使用
<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。此外,还有将对象 marshalling 到 XML 和 unmarshalling 对象的方法。以下是一个向 Web 服务发送简单 XML 消息的示例。
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 向位于 https://:8080/WebService 的 Web 服务发送一个 hello world 消息(在 simpleSendAndReceive() 方法的情况下),并将结果写入控制台。WebServiceTemplate 被注入了默认 URI,该 URI 被使用是因为在 Java 代码中没有显式提供 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。(有关 marshalling 和 unmarshaller 的更多信息,请参考 Spring 文档。)通过使用 marshaller,你的应用程序代码可以专注于正在发送或接收的业务对象,而无需关心它如何表示为 XML 的细节。为了使用 marshalling 功能,你必须使用 WebServiceTemplate 类的 marshaller/unmarshaller 属性设置 marshaller 和 unmarshaller。
为了适应在消息上设置 SOAP 头和其他设置,WebServiceMessageCallback 接口允许你在消息创建之后、发送之前访问消息。下面的示例演示了如何在一个通过 marshalling 对象创建的消息上设置 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。这个 callback 将所需的 Action 头作为参数。它还有构造函数用于指定 WS-Addressing 版本和 To 头。如果未指定,To 头将默认为正在进行的连接的 URL。
以下是一个将 Action 头设置为 http://samples/RequestOrder 的示例
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
WebServiceMessageExtractor 接口是一个低级 callback 接口,允许你完全控制从接收到的 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 服务的类)时,有两种可能的方法
编写单元测试,简单地模拟 (mock) 掉 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 Framework 中提供的标准测试设施。这不是必需的,但这通常是设置测试最简单的方法。 |
|
|
|
在 |
|
我们通过调用 我们还通过调用 测试的这部分可能看起来有点令人困惑,但 IDE 的代码完成功能非常有帮助。输入 |
|
我们在 |
|
我们在 |
为了验证请求消息是否满足某些期望,MockWebServiceServer 使用 RequestMatcher 策略接口。此接口定义的契约非常简单
public interface RequestMatcher {
void match(URI uri,
WebServiceMessage request)
throws IOException,
AssertionError;
}
你可以编写此接口的自己的实现,在消息不符合你的期望时抛出 AssertionError,但你当然不必这样做。RequestMatchers 类提供了标准的 RequestMatcher 实现供你在测试中使用。你通常会静态导入这个类。
RequestMatchers 类提供以下请求匹配器
RequestMatchers 方法 | 描述 |
|---|---|
anything() | 期望任何类型的请求。 |
payload() | 期望给定的请求有效载荷 (payload)。 |
validPayload() | 期望请求有效载荷 (payload) 根据给定的 XSD schema 进行验证。 |
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() | 创建具有给定有效载荷 (payload) 的响应消息。 |
withError() | 在响应连接中创建错误。此方法让你有机会测试错误处理。 |
withException() | 从响应连接读取时抛出异常。此方法让你有机会测试异常处理。 |
withMustUnderstandFault(), withClientOrSenderFault(), withServerOrReceiverFault() 和 withVersionMismatchFault() | 创建具有给定 SOAP 错误的响应消息。此方法让你有机会测试错误处理。 |
有关 RequestMatchers 提供的请求匹配器的更多信息,请参考类级别 Javadoc。