第 3 章. 編寫契約優先的 Web 服務

3.1. 引言

本教程將向您展示如何編寫契約優先的 Web 服務,也就是說,首先使用 XML Schema/WSDL 契約,然後編寫 Java 程式碼來開發 Web 服務。Spring-WS 專注於這種開發風格,本教程將幫助您入門。請注意,本教程的第一部分幾乎不包含 Spring-WS 特有的資訊:它主要關於 XML、XSD 和 WSDL。第二部分重點介紹如何使用 Spring-WS 實現此契約。

在進行契約優先的 Web 服務開發時,最重要的事情是嘗試用 XML 的方式思考。這意味著 Java 語言的概念變得不那麼重要。透過網路傳輸的是 XML,您應該專注於此。使用 Java 來實現 Web 服務是一個實現細節。一個重要的細節,但終究是一個細節。

在本教程中,我們將定義一個由人力資源部門建立的 Web 服務。客戶端可以向該服務傳送請假申請表來預訂假期。

3.2. 訊息

在本節中,我們將重點介紹傳送到 Web 服務和從 Web 服務傳送的實際 XML 訊息。我們將首先確定這些訊息的樣子。

3.2.1. 假期

在此場景中,我們需要處理請假申請,因此確定假期在 XML 中的樣子很有意義

<Holiday xmlns="http://mycompany.com/hr/schemas">
    <StartDate>2006-07-03</StartDate>
    <EndDate>2006-07-07</EndDate>
</Holiday>

假期包括開始日期和結束日期。我們還決定對日期使用標準的ISO 8601日期格式,因為這將省去很多解析麻煩。我們還為元素添加了名稱空間,以確保我們的元素可以在其他 XML 文件中使用。

3.2.2. 員工

在此場景中,還涉及到員工的概念。以下是它在 XML 中的樣子

<Employee xmlns="http://mycompany.com/hr/schemas">
    <Number>42</Number>
    <FirstName>Arjen</FirstName>
    <LastName>Poutsma</LastName>
</Employee>

我們使用了與之前相同的名稱空間。如果此 <Employee/> 元素可在其他場景中使用,則使用不同的名稱空間(例如 http://mycompany.com/employees/schemas)可能更有意義。

3.2.3. 請假申請

假期和員工元素都可以放在一個 <HolidayRequest/>

<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
    <Holiday>
        <StartDate>2006-07-03</StartDate>
        <EndDate>2006-07-07</EndDate>
    </Holiday>
    <Employee>
        <Number>42</Number>
        <FirstName>Arjen</FirstName>
        <LastName>Poutsma</LastName>
    </Employee>
</HolidayRequest>

這兩個元素的順序無關緊要:<Employee/> 也可以是第一個元素。重要的是所有資料都在那裡。事實上,資料是唯一重要的東西:我們正在採取一種資料驅動的方法。

3.3. 資料契約

現在我們已經看到了一些將要使用的 XML 資料示例,將它們正式化為模式是有意義的。此資料契約定義了我們接受的訊息格式。有四種不同的方法來定義這樣的 XML 契約

DTD 的名稱空間支援有限,因此不適合 Web 服務。Relax NG 和 Schematron 當然比 XML Schema 更容易。不幸的是,它們在跨平臺方面沒有得到廣泛支援。我們將使用 XML Schema。

目前建立 XSD 最簡單的方法是從示例文件中推斷出來。任何好的 XML 編輯器或 Java IDE 都提供了此功能。基本上,這些工具使用一些示例 XML 文件,並從中生成一個可以驗證所有文件的模式。最終結果肯定需要完善,但它是一個很好的起點。

使用上述示例,我們得到了以下生成的模式

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
        elementFormDefault="qualified"
        targetNamespace="http://mycompany.com/hr/schemas"
        xmlns:hr="http://mycompany.com/hr/schemas">
    <xs:element name="HolidayRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="hr:Holiday"/>
                <xs:element ref="hr:Employee"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="Holiday">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="hr:StartDate"/>
                <xs:element ref="hr:EndDate"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="StartDate" type="xs:NMTOKEN"/>
    <xs:element name="EndDate" type="xs:NMTOKEN"/>
    <xs:element name="Employee">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="hr:Number"/>
                <xs:element ref="hr:FirstName"/>
                <xs:element ref="hr:LastName"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="Number" type="xs:integer"/>
    <xs:element name="FirstName" type="xs:NCName"/>
    <xs:element name="LastName" type="xs:NCName"/>
</xs:schema>

這個生成的模式顯然可以改進。首先要注意的是,每個型別都有一個根級別的元素宣告。這意味著 Web 服務應該能夠接受所有這些元素作為資料。這是不希望的:我們只想接受一個 <HolidayRequest/>。透過刪除包裝元素標籤(從而保留型別)並內聯結果,我們可以實現這一點。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:hr="http://mycompany.com/hr/schemas"
        elementFormDefault="qualified"
        targetNamespace="http://mycompany.com/hr/schemas">
    <xs:element name="HolidayRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Holiday" type="hr:HolidayType"/>
                <xs:element name="Employee" type="hr:EmployeeType"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="HolidayType">
        <xs:sequence>
            <xs:element name="StartDate" type="xs:NMTOKEN"/>
            <xs:element name="EndDate" type="xs:NMTOKEN"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="EmployeeType">
        <xs:sequence>
            <xs:element name="Number" type="xs:integer"/>
            <xs:element name="FirstName" type="xs:NCName"/>
            <xs:element name="LastName" type="xs:NCName"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

該模式仍然存在一個問題:使用這樣的模式,您可以期望以下訊息透過驗證

<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
    <Holiday>
        <StartDate>this is not a date</StartDate>
        <EndDate>neither is this</EndDate>
    </Holiday>
    <!-- ... -->
</HolidayRequest>

顯然,我們必須確保開始日期和結束日期確實是日期。XML Schema 有一個出色的內建 date 型別,我們可以使用。我們還將 NCName 更改為 string。最後,我們將 <HolidayRequest/> 中的 sequence 更改為 all。這告訴 XML 解析器,<Holiday/><Employee/> 的順序不重要。我們最終的 XSD 現在看起來像這樣

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:hr="http://mycompany.com/hr/schemas"
        elementFormDefault="qualified"
        targetNamespace="http://mycompany.com/hr/schemas">
    <xs:element name="HolidayRequest">
        <xs:complexType>
            <xs:all>
                <xs:element name="Holiday" type="hr:HolidayType"/>                       (1)
                <xs:element name="Employee" type="hr:EmployeeType"/>
            </xs:all>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="HolidayType">
        <xs:sequence>
            <xs:element name="StartDate" type="xs:date"/>
            <xs:element name="EndDate" type="xs:date"/>                                  (2)
        </xs:sequence>                                                                   (2)
    </xs:complexType>
    <xs:complexType name="EmployeeType">
        <xs:sequence>
            <xs:element name="Number" type="xs:integer"/>
            <xs:element name="FirstName" type="xs:string"/>
            <xs:element name="LastName" type="xs:string"/>                               (3)
        </xs:sequence>                                                                   (3)
    </xs:complexType>
</xs:schema>

1

all 告訴 XML 解析器,<Holiday/><Employee/> 的順序不重要。

2

我們對 <StartDate/><EndDate/> 使用 xsd:date 資料型別,它包含年、月和日。

3

xsd:string 用於名字和姓氏。

我們將此檔案儲存為 hr.xsd

3.4. 服務契約

服務契約通常表示為一個WSDL檔案。請注意,在 Spring-WS 中,無需手動編寫 WSDL。基於 XSD 和一些約定,Spring-WS 可以為您建立 WSDL,如標題為第 3.6 節,“實現端點”的一節所述。如果您願意,可以跳到下一節;本節的其餘部分將向您展示如何手動編寫自己的 WSDL。

我們以標準的序言開始我們的 WSDL,並匯入現有的 XSD。為了將模式與定義分開,我們將為 WSDL 定義使用一個單獨的名稱空間:http://mycompany.com/hr/definitions

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:schema="http://mycompany.com/hr/schemas"
                  xmlns:tns="http://mycompany.com/hr/definitions"
                  targetNamespace="http://mycompany.com/hr/definitions">
    <wsdl:types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <xsd:import namespace="http://mycompany.com/hr/schemas" schemaLocation="hr.xsd"/>
        </xsd:schema>
    </wsdl:types>

接下來,我們基於編寫的模式型別新增訊息。我們只有一條訊息:一條包含我們放入模式中的 <HolidayRequest/> 的訊息

    <wsdl:message name="HolidayRequest">
        <wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/>
    </wsdl:message>

我們將訊息作為操作新增到埠型別中

    <wsdl:portType name="HumanResource">
        <wsdl:operation name="Holiday">
            <wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/>
        </wsdl:operation>
    </wsdl:portType>

這就完成了 WSDL 的抽象部分(可以說是介面),剩下具體部分。具體部分包括一個 binding,它告訴客戶端如何呼叫您剛剛定義的操作;以及一個 service,它告訴客戶端在何處呼叫它。

新增具體部分是相當標準的:只需引用您之前定義的抽象部分,確保對 soap:binding 元素使用document/literalrpc/encoded 已棄用),為操作選擇一個 soapAction(在本例中為 http://mycompany.com/RequestHoliday,但任何 URI 都可以),並確定您希望請求進入的 location URL(在本例中為 http://mycompany.com/humanresources

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:schema="http://mycompany.com/hr/schemas"
                  xmlns:tns="http://mycompany.com/hr/definitions"
                  targetNamespace="http://mycompany.com/hr/definitions">
    <wsdl:types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <xsd:import namespace="http://mycompany.com/hr/schemas"                      (1)
                schemaLocation="hr.xsd"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="HolidayRequest">                                                 (2)
        <wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/>               (3)
    </wsdl:message>
    <wsdl:portType name="HumanResource">                                                 (4)
        <wsdl:operation name="Holiday">
            <wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/>             (2)
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="HumanResourceBinding" type="tns:HumanResource">                  (4)(5)
        <soap:binding style="document"                                                   (6)
            transport="http://schemas.xmlsoap.org/soap/http"/>                           (7)
        <wsdl:operation name="Holiday">
            <soap:operation soapAction="http://mycompany.com/RequestHoliday"/>           (8)
            <wsdl:input name="HolidayRequest">
                <soap:body use="literal"/>                                               (6)
            </wsdl:input>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="HumanResourceService">
        <wsdl:port binding="tns:HumanResourceBinding" name="HumanResourcePort">          (5)
            <soap:address location="https://:8080/holidayService/"/>             (9)
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

1

我們匯入第 3.3 節,“資料契約”中定義的模式。

2

我們定義 HolidayRequest 訊息,該訊息在 portType 中使用。

3

HolidayRequest 型別在模式中定義。

4

我們定義 HumanResource 埠型別,該埠型別在 binding 中使用。

5

我們定義 HumanResourceBinding 繫結,該繫結在 port 中使用。

6

我們使用 document/literal 風格。

7

字面值 http://schemas.xmlsoap.org/soap/http 表示 HTTP 傳輸。

8

soapAction 屬性表示將隨每個請求傳送的 SOAPAction HTTP 頭部。

9

https://:8080/holidayService/ 地址是呼叫 Web 服務的 URL。

這是最終的 WSDL。我們將在下一節中描述如何實現由此產生的模式和 WSDL。

3.5. 建立專案

在本節中,我們將使用Maven3為我們建立初始專案結構。這樣做不是必需的,但極大地減少了我們為設定 HolidayService 所需編寫的程式碼量。

以下命令使用 Spring-WS 原型(即專案模板)為我們建立一個 Maven3 Web 應用程式專案

mvn archetype:create -DarchetypeGroupId=org.springframework.ws \
  -DarchetypeArtifactId=spring-ws-archetype \
  -DarchetypeVersion=2.1.4.RELEASE \
  -DgroupId=com.mycompany.hr \
  -DartifactId=holidayService

此命令將建立一個名為 holidayService 的新目錄。在此目錄中,有一個 'src/main/webapp' 目錄,它將包含 WAR 檔案的根目錄。您將在此處找到標準的 Web 應用程式部署描述符 'WEB-INF/web.xml',它定義了一個 Spring-WS MessageDispatcherServlet 並將所有傳入請求對映到此 Servlet。

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">

    <display-name>MyCompany HR Holiday Service</display-name>

    <!-- take especial notice of the name of this servlet -->
    <servlet>
        <servlet-name>spring-ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-ws</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

除了上述 'WEB-INF/web.xml' 檔案外,您還需要另一個 Spring-WS 特有的配置檔案,名為 'WEB-INF/spring-ws-servlet.xml'。此檔案包含所有 Spring-WS 特有的 Bean,例如 EndPointsWebServiceMessageReceivers 等等,並用於建立一個新的 Spring 容器。此檔案的名稱派生自伴隨 Servlet 的名稱(在本例中為 'spring-ws'),並附加 '-servlet.xml'。因此,如果您定義了一個名為 'dynamite'MessageDispatcherServlet,則 Spring-WS 特有的配置檔案的名稱將是 'WEB-INF/dynamite-servlet.xml'

(您可以在???中檢視此示例的 'WEB-INF/spring-ws-servlet.xml' 檔案內容。)

建立專案結構後,您可以將上一節的模式和 WSDL 放入 'WEB-INF/' 資料夾。

3.6. 實現端點

在 Spring-WS 中,您將實現端點(Endpoints)來處理傳入的 XML 訊息。通常透過使用 @Endpoint 註解來標註類來建立端點。在此端點類中,您將建立一個或多個處理傳入請求的方法。方法簽名可以非常靈活:您可以包含幾乎任何與傳入 XML 訊息相關的引數型別,這將在後面解釋。

3.6.1. 處理 XML 訊息

在此示例應用程式中,我們將使用JDom來處理 XML 訊息。我們還在使用XPath,因為它允許我們選擇 XML JDOM 樹的特定部分,而無需嚴格的模式一致性。

package com.mycompany.hr.ws;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;

import com.mycompany.hr.service.HumanResourceService;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;

@Endpoint                                                                                (1)
public class HolidayEndpoint {

  private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";

  private XPath startDateExpression;

  private XPath endDateExpression;

  private XPath nameExpression;

  private HumanResourceService humanResourceService;

  @Autowired
  public HolidayEndpoint(HumanResourceService humanResourceService)                      (2)
      throws JDOMException {
    this.humanResourceService = humanResourceService;

    Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);

    startDateExpression = XPath.newInstance("//hr:StartDate");
    startDateExpression.addNamespace(namespace);

    endDateExpression = XPath.newInstance("//hr:EndDate");
    endDateExpression.addNamespace(namespace);

    nameExpression = XPath.newInstance("concat(//hr:FirstName,' ',//hr:LastName)");
    nameExpression.addNamespace(namespace);
  }

  @PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")                  (3)
  public void handleHolidayRequest(@RequestPayload Element holidayRequest)               (4)
      throws Exception {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    Date startDate = dateFormat.parse(startDateExpression.valueOf(holidayRequest));
    Date endDate = dateFormat.parse(endDateExpression.valueOf(holidayRequest));
    String name = nameExpression.valueOf(holidayRequest);

    humanResourceService.bookHoliday(startDate, endDate, name);
  }

}

1

HolidayEndpoint@Endpoint 註解進行了標註。這使得該類成為一種特殊的 @Component,適用於在 Spring-WS 中處理 XML 訊息,並使其適合進行元件掃描。

2

HolidayEndpoint 需要 HumanResourceService 業務服務來操作,因此我們透過建構函式注入依賴項,並使用 @Autowired 註解進行標註。接下來,我們使用 JDOM API 設定 XPath 表示式。有三個表示式://hr:StartDate 用於提取 <StartDate> 的文字值,//hr:EndDate 用於提取結束日期,以及 concat(//hr:FirstName,' ',//hr:LastName) 用於提取和連線員工姓名。

3

@PayloadRoot 註解告訴 Spring-WS,handleHolidayRequest 方法適用於處理 XML 訊息。該方法可以處理的訊息型別由註解值指定,在本例中,它可以處理具有 HolidayRequest 本地部分和 http://mycompany.com/hr/schemas 名稱空間的 XML 元素。有關將訊息對映到端點的更多資訊,請參見下一節。

4

handleHolidayRequest(..) 方法是主要的處理方法,它接收傳入 XML 訊息中的 <HolidayRequest/> 元素。 @RequestPayload 註解表示holidayRequest 引數應對映到請求訊息的有效載荷。我們使用 XPath 表示式從 XML 訊息中提取字串值,並使用 SimpleDateFormat 將這些值轉換為 Date 物件。利用這些值,我們呼叫業務服務上的方法。通常,這將導致啟動資料庫事務,並更改資料庫中的一些記錄。最後,我們定義一個 void 返回型別,這表示 Spring-WS 不想傳送響應訊息。如果我們想要一個響應訊息,我們可以返回一個表示響應訊息有效載荷的 JDOM Element。

使用 JDOM 只是處理 XML 的選項之一:其他選項包括 DOM、dom4j、XOM、SAX 和 StAX,此外還有編組技術,如 JAXB、Castor、XMLBeans、JiBX 和 XStream,這在下一章中會解釋。我們選擇 JDOM 是因為它允許我們訪問原始 XML,並且它基於類(不像 W3C DOM 和 dom4j 那樣基於介面和工廠方法),這使得程式碼更簡潔。我們使用 XPath 是因為它比編組技術更不易出錯:我們不關心嚴格的模式一致性,只要我們能找到日期和名稱即可。

由於我們使用 JDOM,必須將一些依賴項新增到位於專案根目錄的 Maven pom.xml 檔案中。以下是 POM 的相關部分

<dependencies>
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-ws-core</artifactId>
        <version>2.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>jdom</groupId>
        <artifactId>jdom</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
        <version>1.1</version>
    </dependency>
</dependencies>

以下是我們如何在我們的 spring-ws-servlet.xml Spring XML 配置檔案中使用元件掃描來配置這些類。我們還透過 <sws:annotation-driven> 元素指示 Spring-WS 使用註解驅動的端點。

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

  <context:component-scan base-package="com.mycompany.hr"/>

  <sws:annotation-driven/>

</beans>

3.6.2. 將訊息路由到端點

在編寫端點的過程中,我們還使用了 @PayloadRoot 註解來指示 handleHolidayRequest 方法可以處理哪類訊息。在 Spring-WS 中,此過程由 EndpointMapping 負責。在此,我們透過使用 PayloadRootAnnotationMethodEndpointMapping 根據訊息內容進行路由。上面使用的註解

@PayloadRoot(namespace = "http://mycompany.com/hr/schemas", localPart = "HolidayRequest")

基本意味著,無論何時收到具有名稱空間 http://mycompany.com/hr/schemas 和本地名稱 HolidayRequest 的 XML 訊息,它都將被路由到 handleHolidayRequest 方法。透過在我們的配置中使用 <sws:annotation-driven> 元素,我們啟用了對 @PayloadRoot 註解的檢測。在一個端點中擁有多個相關的處理方法是可能的(並且很常見),每個方法處理不同的 XML 訊息。

還有其他將端點對映到 XML 訊息的方法,這些方法將在下一章中描述。

3.6.3. 提供服務和存根實現

現在我們有了端點,我們需要 HumanResourceService 及其實現供 HolidayEndpoint 使用。

package com.mycompany.hr.service;

import java.util.Date;

public interface HumanResourceService {
    void bookHoliday(Date startDate, Date endDate, String name);
}

出於教程目的,我們將使用 HumanResourceService 的一個簡單存根實現。

package com.mycompany.hr.service;

import java.util.Date;

import org.springframework.stereotype.Service;

@Service                                                                                 (1)
public class StubHumanResourceService implements HumanResourceService {
    public void bookHoliday(Date startDate, Date endDate, String name) {
        System.out.println("Booking holiday for [" + startDate + "-" + endDate + "] for [" + name + "] ");
    }
}

1

StubHumanResourceService@Service 註解進行了標註。這使得該類成為業務外觀,使其成為在 HolidayEndpoint 中由 @Autowired 注入的候選項。

3.7. 釋出 WSDL

最後,我們需要釋出 WSDL。如第 3.4 節,“服務契約”中所述,我們不需要自己編寫 WSDL;Spring-WS 可以基於一些約定為我們生成一個。以下是我們如何定義生成過程

<sws:dynamic-wsdl id="holiday"                                                           (1)
    portTypeName="HumanResource"                                                         (3)
    locationUri="/holidayService/"                                                       (4)
    targetNamespace="http://mycompany.com/hr/definitions">                               (5)
  <sws:xsd location="/WEB-INF/hr.xsd"/>                                                  (2)
</sws:dynamic-wsdl>

1

ID 決定了可以檢索 WSDL 的 URL。在本例中,ID 是 holiday,這意味著可以在 Servlet 上下文中以 holiday.wsdl 的形式檢索 WSDL。完整的 URL 通常是 https://:8080/holidayService/holiday.wsdl

3

接下來,我們將 WSDL 埠型別設定為 HumanResource

4

我們設定了服務可訪問的位置:/holidayService/。我們使用相對 URI,並指示框架將其動態轉換為絕對 URI。因此,如果服務部署到不同的上下文,我們無需手動更改 URI。更多資訊,請參閱第 5.3.1.1 節,“自動 WSDL 暴露”

為了使位置轉換生效,我們需要在 web.xml 中的 spring-ws servlet 中新增一個初始化引數

<init-param>
  <param-name>transformWsdlLocations</param-name>
  <param-value>true</param-value>
</init-param>

5

我們定義 WSDL 定義本身的目標名稱空間。設定此屬性不是必需的。如果未設定,WSDL 將具有與 XSD 模式相同的名稱空間。

2

xsd 元素引用了我們在第 3.3 節,“資料契約”中定義的人力資源模式。我們只需將模式放在應用程式的 WEB-INF 目錄中。

您可以使用mvn install建立 WAR 檔案。如果您部署應用程式(到 Tomcat、Jetty 等),並將瀏覽器指向此位置,您將看到生成的 WSDL。此 WSDL 可供客戶端使用,例如soapUI或其他 SOAP 框架。

本教程到此結束。教程程式碼可在 Spring-WS 的完整發行版中找到。下一步是檢視發行版中的 echo 示例應用程式。之後,檢視 airline 示例,它稍微複雜一些,因為它使用了 JAXB、WS-Security、Hibernate 和事務服務層。最後,您可以閱讀其餘的參考文件。