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。