第 4 章。共享元件

在本章中,我們將探討在客戶端和伺服器端 Spring-WS 開發之間共享的元件。這些介面和類代表了 Spring-WS 的構建塊,因此瞭解它們的作用很重要,即使您不直接使用它們。

4.1。Web 服務訊息

4.1.1。WebServiceMessage

Spring Web Services 的核心介面之一是 WebServiceMessage。此介面表示協議無關的 XML 訊息。該介面包含方法,允許以 javax.xml.transform.Sourcejavax.xml.transform.Result 的形式訪問訊息的負載。SourceResult 是標記介面,代表 XML 輸入和輸出的抽象。具體實現封裝了各種 XML 表示形式,如下表所示。

Source/Result 實現封裝 XML 表示
javax.xml.transform.dom.DOMSourceorg.w3c.dom.Node
javax.xml.transform.dom.DOMResultorg.w3c.dom.Node
javax.xml.transform.sax.SAXSourceorg.xml.sax.InputSourceorg.xml.sax.XMLReader
javax.xml.transform.sax.SAXResultorg.xml.sax.ContentHandler
javax.xml.transform.stream.StreamSource java.io.Filejava.io.InputStreamjava.io.Reader
javax.xml.transform.stream.StreamResult java.io.Filejava.io.OutputStreamjava.io.Writer

除了讀寫負載之外,Web 服務訊息還可以將自身寫入輸出流。

4.1.2。SoapMessage

SoapMessageWebServiceMessage 的子類。它包含 SOAP 特定的方法,例如獲取 SOAP Headers、SOAP Faults 等。通常,您的程式碼不應依賴於 SoapMessage,因為可以透過 WebServiceMessage 中的 getPayloadSource()getPayloadResult() 來獲取 SOAP Body(訊息負載)的內容。只有在需要執行 SOAP 特定的操作時,例如新增標頭、獲取附件等,才需要將 WebServiceMessage 強制轉換為 SoapMessage

4.1.3。訊息工廠

具體的實現訊息是由 WebServiceMessageFactory 建立的。此工廠可以建立一個空訊息,或根據輸入流讀取訊息。WebServiceMessageFactory 有兩個具體的實現;一個基於 SAAJ,即 Java 的 SOAP with Attachments API,另一個基於 Axis 2 的 AXIOM,即 AXis Object Model。

4.1.3.1。SaajSoapMessageFactory

SaajSoapMessageFactory 使用 Java 的 SOAP with Attachments API 建立 SoapMessage 實現。SAAJJ2EE1.4 的一部分,因此應該在大多數現代應用程式伺服器下得到支援。以下是常見應用程式伺服器提供的SAAJ版本概覽

應用程式伺服器SAAJ版本
BEA WebLogic 81.1
BEA WebLogic 91.1/1.2[a]
IBM WebSphere 61.2
SUN Glassfish 11.3

[a] Weblogic 9 在SAAJ1.2 實現中有一個已知的 bug:它實現了所有 1.2 介面,但在呼叫時會丟擲 UnsupportedOperationException。Spring Web Services 有一個解決方法:它在 WebLogic 9 上執行時使用SAAJ1.1。

此外,Java SE 6 包括SAAJ1.3。您可以透過以下方式配置 SaajSoapMessageFactory

<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />

備註

SAAJ基於 DOM,即 Document Object Model。這意味著所有 SOAP 訊息都儲存在記憶體中。對於較大的 SOAP 訊息,這可能不太高效。在這種情況下,AxiomSoapMessageFactory 可能更適用。

4.1.3.2。AxiomSoapMessageFactory

AxiomSoapMessageFactory 使用 AXis 2 Object Model 建立 SoapMessage 實現。AXIOM基於StAX,即 XML 流式 API。StAX 提供了一種基於拉取的 XML 訊息讀取機制,對於較大的訊息可能更有效。

要提高 AxiomSoapMessageFactory 的讀取效能,您可以將 payloadCaching 屬性設定為 false(預設為 true)。這將直接從套接字流中讀取 SOAP 主體的內容。啟用此設定後,負載只能讀取一次。這意味著您必須確保任何預處理(日誌記錄等)訊息的操作都不會消耗它。

您可以使用 AxiomSoapMessageFactory 如下

<bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
    <property name="payloadCaching" value="true"/>
</bean>

除了負載快取之外,AXIOM還支援完整的流式訊息,如 StreamingWebServiceMessage 中定義的。這意味著負載可以直接設定在響應訊息上,而不是寫入 DOM 樹或緩衝區。

AXIOM的完整流式處理在處理程式方法返回一個JAXB2-支援的物件時使用。它將自動將此已編組的物件設定到響應訊息中,並在響應外出時將其寫入出站套接字流。

有關完整流式處理的更多資訊,請參閱 StreamingWebServiceMessageStreamingPayload 的類級別 Javadoc。

4.1.3.3. SOAP1.1 或 1.2

SaajSoapMessageFactoryAxiomSoapMessageFactory 都有一個 soapVersion 屬性,您可以在其中注入一個 SoapVersion 常量。預設情況下,版本是 1.1,但您可以像這樣將其設定為 1.2

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-2.0.xsd">

    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
        </property>
    </bean>

</beans>

在上面的示例中,我們定義了一個只接受SOAP1.2 訊息的 SaajSoapMessageFactory

注意

儘管SOAP的兩個版本在格式上非常相似,但 1.2 版本與 1.1 不向後相容,因為它使用了不同的 XML 名稱空間。其他主要區別包括SOAP1.1 和 1.2 的 Fault 結構不同,以及 SOAPAction HTTP 標頭實際上已被棄用,儘管它們仍然有效。

一個重要的注意事項是SOAP版本號,或者通常的 WS-* 規範版本號,是規範的最新版本通常不是最受歡迎的版本。對於SOAP,這意味著目前最好的版本是 1.1。版本 1.2 未來可能會更受歡迎,但目前 1.1 是最安全的選擇。

4.1.4。MessageContext

通常,訊息成對出現:請求和響應。請求在客戶端建立,透過某種傳輸傳送到伺服器端,然後在伺服器端生成響應。此響應傳送回客戶端,並在客戶端讀取。

在 Spring Web Services 中,這種對話包含在 MessageContext 中,該上下文具有獲取請求和響應訊息的屬性。在客戶端,訊息上下文由 WebServiceTemplate 建立。在伺服器端,訊息上下文從特定於傳輸的輸入流中讀取。例如,在 HTTP 中,它從 HttpServletRequest 讀取,響應寫回 HttpServletResponse

4.2。TransportContext

SOAP 協議的一個關鍵屬性是它試圖保持傳輸無關。這就是為什麼 Spring-WS 不支援透過 HTTP 請求 URL 將訊息對映到端點,而是透過訊息內容對映。

然而,有時有必要在客戶端或伺服器端訪問底層傳輸。為此,Spring Web Services 提供了 TransportContext。傳輸上下文允許訪問底層的 WebServiceConnection,它通常是伺服器端的 HttpServletConnection;或者客戶端的 HttpUrlConnectionCommonsHttpConnection。例如,您可以在伺服器端終結點或攔截器中像這樣獲取當前請求的 IP 地址

TransportContext context = TransportContextHolder.getTransportContext();
HttpServletConnection connection = (HttpServletConnection )context.getConnection();
HttpServletRequest request = connection.getHttpServletRequest();
String ipAddress = request.getRemoteAddr();

4.3。使用 XPath 處理 XML

處理 XML 的最佳方法之一是使用 XPath。引用 [effective-xml],第 35 項

 

XPath 是一種第四代宣告式語言,它允許您指定要處理的節點,而無需精確說明處理器應如何導航到這些節點。XPath 的資料模型非常適合支援幾乎所有開發人員從 XML 中想要的功能。例如,它合併了所有相鄰的文字(包括 CDATA 部分中的文字),允許計算值來跳過註釋和處理指令,幷包含子元素和後代元素的文字,並且要求解析所有外部實體引用。實際上,XPath 表示式往往對輸入文件中意外但可能不重要的更改更具魯棒性。

 
 --Elliotte Rusty Harold

Spring Web Services 在您的應用程式中有兩種使用 XPath 的方法:更快的 XPathExpression 或更靈活的 XPathTemplate

4.3.1。XPathExpression

XPathExpression 是編譯後的 XPath 表示式的抽象,例如 Java 5 的 javax.xml.xpath.XPathExpression 或 Jaxen 的 XPath 類。要在應用程式上下文中構造表示式,可以使用 XPathExpressionFactoryBean。以下是一個使用此工廠 bean 的示例

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean id="nameExpression" class="org.springframework.xml.xpath.XPathExpressionFactoryBean">
        <property name="expression" value="/Contacts/Contact/Name"/>
    </bean>

    <bean id="myEndpoint" class="sample.MyXPathClass">
        <constructor-arg ref="nameExpression"/>
    </bean>

</beans>

上面的表示式不使用名稱空間,但我們可以使用工廠 bean 的 namespaces 屬性來設定它們。表示式可以在程式碼中像這樣使用

package sample;

public class MyXPathClass {

    private final XPathExpression nameExpression;

    public MyXPathClass(XPathExpression nameExpression) {
        this.nameExpression = nameExpression;
    }

    public void doXPath(Document document) {
        String name = nameExpression.evaluateAsString(document.getDocumentElement());
        System.out.println("Name: " + name);
    }

}

對於更靈活的方法,您可以使用 NodeMapper,它類似於 Spring JDBC 支援中的 RowMapper。以下示例顯示瞭如何使用它

package sample;

public class MyXPathClass  {

   private final XPathExpression contactExpression;

   public MyXPathClass(XPathExpression contactExpression) {
      this.contactExpression = contactExpression;
   }

   public void doXPath(Document document) {
      List contacts = contactExpression.evaluate(document,
        new NodeMapper() {
           public Object mapNode(Node node, int nodeNum) throws DOMException {
              Element contactElement = (Element) node;
              Element nameElement = (Element) contactElement.getElementsByTagName("Name").item(0);
              Element phoneElement = (Element) contactElement.getElementsByTagName("Phone").item(0);
              return new Contact(nameElement.getTextContent(), phoneElement.getTextContent());
           }
        });
      // do something with list of Contact objects
   }
}

與 Spring JDBC 的 RowMapper 中對映行類似,每個結果節點都使用匿名內部類進行對映。在這種情況下,我們建立一個 Contact 物件,我們稍後會用到它。

4.3.2。XPathTemplate

XPathExpression 只允許您評估單個預編譯的表示式。更靈活但較慢的替代方案是 XpathTemplate。此類遵循 Spring 中使用的常見模板模式(JdbcTemplate、JmsTemplate 等)。以下是一個示例

package sample;

public class MyXPathClass {

    private XPathOperations template = new Jaxp13XPathTemplate();

    public void doXPath(Source source) {
        String name = template.evaluateAsString("/Contacts/Contact/Name", request);
        // do something with name
    }

}

4.4。訊息日誌記錄和跟蹤

在開發或除錯 Web 服務時,檢視訊息到達時或傳送之前的內容可能非常有用。Spring Web Services 透過標準的 Commons Logging 介面提供此功能。

注意

確保使用 Commons Logging 版本 1.1 或更高版本。早期版本存在類載入問題,並且與 Log4J TRACE 級別不整合。

要記錄所有伺服器端訊息,只需將 org.springframework.ws.server.MessageTracing 日誌記錄器設定為 DEBUG 或 TRACE 級別。在 debug 級別,只記錄負載的根元素;在 TRACE 級別,記錄整個訊息內容。如果您只想記錄傳送的訊息,請使用 org.springframework.ws.server.MessageTracing.sent 日誌記錄器;或者使用 org.springframework.ws.server.MessageTracing.received 來記錄接收的訊息。

在客戶端,存在類似的日誌記錄器:org.springframework.ws.client.MessageTracing.sentorg.springframework.ws.client.MessageTracing.received

這是一個示例 log4j.properties 配置,它記錄客戶端傳送訊息的完整內容,以及客戶端接收訊息的負載根元素。在伺服器端,傳送和接收的訊息的負載根都會被記錄

log4j.rootCategory=INFO, stdout
log4j.logger.org.springframework.ws.client.MessageTracing.sent=TRACE
log4j.logger.org.springframework.ws.client.MessageTracing.received=DEBUG

log4j.logger.org.springframework.ws.server.MessageTracing=DEBUG

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p [%c{3}] %m%n

使用此配置,典型輸出將是

TRACE [client.MessageTracing.sent] Sent request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="...
DEBUG [server.MessageTracing.received] Received request [SaajSoapMessage {http://example.com}request] ...
DEBUG [server.MessageTracing.sent] Sent response [SaajSoapMessage {http://example.com}response] ...
DEBUG [client.MessageTracing.received] Received response [SaajSoapMessage {http://example.com}response] ...
© . This site is unofficial and not affiliated with VMware.