Spring-WS 提供了一个客户端 Web 服务 API,允许以一致的、XML 驱动的 방식으로 Web 服务进行访问。它还支持使用编组器和反编组器,以便您的服务层代码可以专门处理 Java 对象。
org.springframework.ws.client.core 包提供了使用客户端访问 API 的核心功能。它包含简化 Web 服务使用的模板类,就像核心 Spring JdbcTemplate
简化 JDBC 一样。Spring 模板类的共同设计原则是提供帮助方法来执行常见操作,而对于更复杂的使用,则委托给用户实现的回调接口。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,它至少包含 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://127.0.0.1?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://127.0.0.1:8080/AnotherWebService", source, result); } }
<beans xmlns="http://www.springframework.org/schema/beans"> <bean id="webServiceClient" class="WebServiceClient"> <property name="defaultUri" value="https://127.0.0.1:8080/WebService"/> </bean> </beans>
上面的示例使用 WebServiceTemplate
将 hello world 消息发送到位于 https://127.0.0.1:8080/WebService
的 Web 服务(对于 simpleSendAndReceive()
方法),并将结果写入控制台。WebServiceTemplate
注入默认 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
。(有关编组和反编组的更多信息,请参阅 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。