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。