第 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

我们使用xsd:date数据类型(由年份、月份和日期组成)表示<StartDate/><EndDate/>

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://127.0.0.1:8080/holidayService/"/>             (9)
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

1

我们导入在第 3.3 节“数据契约”中定义的模式。

2

我们定义HolidayRequest消息,它将在portType中使用。

3

HolidayRequest类型在模式中定义。

4

我们定义HumanResource端口类型,它将在binding中使用。

5

我们定义HumanResourceBinding绑定,它将在port中使用。

6

我们使用文档/文字样式。

7

文字http://schemas.xmlsoap.org/soap/http表示 HTTP 传输。

8

soapAction属性表示将随每个请求一起发送的SOAPAction HTTP 标头。

9

https://127.0.0.1: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 中,您将实现端点来处理传入的 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 返回类型,这表示我们不想发送响应消息。如果我们想要一个响应消息,我们可以返回一个表示响应消息有效负载的 JDOM 元素。

使用 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 配置文件中,使用组件扫描来配置这些类的方式。我们还指示 Spring-WS 使用注解驱动的端点,使用 <sws:annotation-driven> 元素。

<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/schemasHolidayRequest 本地名称的 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://127.0.0.1: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 的完整发行版中找到。下一步是查看发行版中包含的回显示例应用程序。之后,查看航空公司示例,它稍微复杂一些,因为它使用了 JAXB、WS-Security、Hibernate 和事务性服务层。最后,您可以阅读参考文档的其余部分。