第三章. 编写契约优先的 Web 服务

3.1. 简介

本教程将向您展示如何编写契约优先的 Web 服务,即先编写 XML Schema/WSDL 契约,再编写 Java 代码。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 的此类契约

DTDs 对命名空间的支持有限,因此不适用于 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://: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://: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 返回类型,这向 Spring-WS 表明我们不想发送响应消息。如果我们想要响应消息,我们可以返回一个表示响应消息有效载荷的 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 配置文件中使用组件扫描来配置这些类。我们还通过 <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 可以根据一些约定为我们生成 WSDL。以下是我们定义生成的方式

<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,这意味着 WSDL 可以在 servlet 上下文中以 holiday.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 参数

<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 和事务性服务层。最后,您可以阅读其余的参考文档。

© . This site is unofficial and not affiliated with VMware.