Creating a SOAP Web Service with Spring – 用Spring创建一个SOAP网络服务

最后修改: 2019年 2月 13日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn how to create a SOAP-based web service with Spring Boot Starter Web Services.

在本教程中,我们将学习如何用Spring Boot Starter Web Services创建一个基于SOAP的Web服务

2. SOAP Web Services

<2.SOAP网络服务

In short, a web service is a machine-to-machine, platform independent service that allows communication over a network.

简而言之,网络服务是一种机器对机器、独立于平台的服务,允许通过网络进行通信。

SOAP is a messaging protocol. Messages (requests and responses) are XML documents over HTTPThe XML contract is defined by the WSDL (Web Services Description Language). It provides a set of rules to define the messages, bindings, operations, and location of the service.

SOAP是一个消息传递协议。消息(请求和响应)是通过HTTP的XML文档XML合约由WSDL(网络服务描述语言)定义。它提供了一套规则来定义消息、绑定、操作和服务的位置。

The XML used in SOAP can become extremely complex. For this reason, it’s best to use SOAP with a framework, like JAX-WS or Spring, as we’ll see in this tutorial.

SOAP中使用的XML会变得非常复杂。出于这个原因,最好将SOAP与框架一起使用,如JAX-WS或Spring,我们将在本教程中看到这一点。

3. Contract-First Development Style

3.合同第一的开发风格

There are two possible approaches when creating a web service: Contract-Last and Contract-First. When we use a contract-last approach, we start with the Java code, and generate the web service contract (WSDL) from the classes. When using contract-first, we start with the WSDL contract, from which we generate the Java classes.

在创建网络服务时,有两种可能的方法。Contract-LastContract-First。当我们使用合同先行的方法时,我们从Java代码开始,并从类中生成Web服务合同(WSDL)。当使用契约优先时,我们从WSDL契约开始,并从其生成Java类。

Spring-WS only supports the contract-first development style.

Spring-WS只支持契约优先的开发风格。

4. Setting Up the Spring Boot Project

4.设置Spring Boot项目

We’ll create a Spring Boot project where we’ll define our SOAP WS server.

我们将创建一个Spring Boot项目,在那里我们将定义我们的SOAP WS服务器。

4.1. Maven Dependencies

4.1.Maven的依赖性

Let’s start by adding the spring-boot-starter-parent to our project:

让我们先把spring-boot-starter-parent添加到我们的项目。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.2</version>
</parent>

Next, let’s add the spring-boot-starter-web-services and wsdl4j dependencies:

接下来,让我们添加spring-boot-starter-web-serviceswsdl4j依赖项。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
    <groupId>wsdl4j</groupId>
    <artifactId>wsdl4j</artifactId>
</dependency>

4.2. The XSD File

4.2.XSD文件

The contract-first approach requires us to create the domain (methods and parameters) for our service first. We’ll use an XML schema file (XSD) that Spring-WS will export automatically as a WSDL:

契约优先的方法要求我们首先为我们的服务创建域(方法和参数)。我们将使用一个XML模式文件(XSD),Spring-WS会将其自动导出为一个WSDL。

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.baeldung.com/springsoap/gen"
           targetNamespace="http://www.baeldung.com/springsoap/gen" elementFormDefault="qualified">

    <xs:element name="getCountryRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="getCountryResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="country" type="tns:country"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="country">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="population" type="xs:int"/>
            <xs:element name="capital" type="xs:string"/>
            <xs:element name="currency" type="tns:currency"/>
        </xs:sequence>
    </xs:complexType>

    <xs:simpleType name="currency">
        <xs:restriction base="xs:string">
            <xs:enumeration value="GBP"/>
            <xs:enumeration value="EUR"/>
            <xs:enumeration value="PLN"/>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

In this file, we can see the format of the getCountryRequest web service request. We’ll define it to accept one parameter of the type string.

在这个文件中,我们可以看到getCountryRequest网络服务请求的格式。我们将把它定义为接受一个string类型的参数。

Next, we’ll define the format of the response, which contains an object of the type country.

接下来,我们将定义响应的格式,它包含一个类型为country的对象。

Finally, we can see the currency object used within the country object.

最后,我们可以看到currency对象在country对象内使用。

4.3. Generate the Domain Java Classes

4.3.生成领域的Java类

Now we’ll generate the Java classes from the XSD file defined in the previous section. The jaxb2-maven-plugin will do this automatically during build time. The plugin uses the XJC tool as a code generation engine. XJC compiles the XSD schema file into fully annotated Java classes.

现在我们要根据上一节定义的XSD文件生成Java类。jaxb2-maven-plugin将在构建时自动完成这一工作。该插件使用XJC工具作为代码生成引擎。XJC将XSD模式文件编译成完全注解的Java类。

Let’s add and configure the plugin in our pom.xml:

让我们在pom.xml中添加并配置该插件。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>xjc</id>
            <goals>
                <goal>xjc</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
        <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
        <clearOutputDir>false</clearOutputDir>
    </configuration>
</plugin>

Here we notice two important configurations:

这里我们注意到两个重要的配置。

  • <schemaDirectory>${project.basedir}/src/main/resources</schemaDirectory> – The location of the XSD file
  • <outputDirectory>${project.basedir}/src/main/java</outputDirectory> – Where we want our Java code to be generated to

To generate the Java classes, we could use the XJC tool from our Java installation. It’s even more simple in our Maven project though, as the classes will be automatically generated during the usual Maven build:

为了生成Java类,我们可以使用Java安装中的XJC工具。不过在我们的Maven项目中就更简单了,因为这些类将在通常的Maven构建过程中自动生成

mvn compile

4.4. Add the SOAP Web Service Endpoint

4.4.添加SOAP网络服务端点

The SOAP web service endpoint class will handle all the incoming requests for the service. It’ll initiate the processing, and send the response back.

SOAP网络服务端点类将处理所有传入的服务请求。它将发起处理,并将响应送回。

Before defining this, we’ll create a Country repository in order to provide data to the web service:

在定义这个之前,我们将创建一个Country资源库,以便为网络服务提供数据。

@Component
public class CountryRepository {

    private static final Map<String, Country> countries = new HashMap<>();

    @PostConstruct
    public void initData() {
        // initialize countries map
    }

    public Country findCountry(String name) {
        return countries.get(name);
    }
}

Next, we’ll configure the endpoint:

接下来,我们将配置端点。

@Endpoint
public class CountryEndpoint {

    private static final String NAMESPACE_URI = "http://www.baeldung.com/springsoap/gen";

    private CountryRepository countryRepository;

    @Autowired
    public CountryEndpoint(CountryRepository countryRepository) {
        this.countryRepository = countryRepository;
    }

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
    @ResponsePayload
    public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) {
        GetCountryResponse response = new GetCountryResponse();
        response.setCountry(countryRepository.findCountry(request.getName()));

        return response;
    }
}

Here are a few details to notice:

这里有几个细节需要注意。

  • @Endpoint – registers the class with Spring WS as a Web Service Endpoint
  • @PayloadRootdefines the handler method according to the namespace and localPart attributes
  • @ResponsePayload – indicates that this method returns a value to be mapped to the response payload
  • @RequestPayload – indicates that this method accepts a parameter to be mapped from the incoming request

4.5. The SOAP Web Service Configuration Beans

4.5.SOAP网络服务配置Bean

Now let’s create a class for configuring the Spring message dispatcher servlet to receive the request:

现在让我们创建一个类来配置Spring消息调度器servlet来接收请求。

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
    // bean definitions
}

@EnableWs enables SOAP Web Service features in this Spring Boot application. The WebServiceConfig class extends the WsConfigurerAdapter base class, which configures the annotation-driven Spring-WS programming model.

@EnableWs在这个Spring Boot应用程序中启用SOAP Web服务功能。WebServiceConfig类扩展了WsConfigurerAdapter基类,它配置了注释驱动的Spring-WS编程模型。

Let’s create a MessageDispatcherServlet, which is used for handling SOAP requests:

让我们创建一个MessageDispatcherServlet,它用于处理SOAP请求。

@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
    MessageDispatcherServlet servlet = new MessageDispatcherServlet();
    servlet.setApplicationContext(applicationContext);
    servlet.setTransformWsdlLocations(true);
    return new ServletRegistrationBean(servlet, "/ws/*");
}

We’ll set the injected ApplicationContext object of the servlet, so that Spring-WS can find other Spring beans.

我们将设置注入的servlet的ApplicationContext对象,以便Spring-WS能够找到其他Spring Bean。

We’ll also enable the WSDL location servlet transformation. This transforms the location attribute of soap:address in the WSDL so that it reflects the URL of the incoming request.

我们还将启用WSDL位置servlet转换。这将转换WSDL中soap:address的位置属性,使其反映传入请求的URL。

Finally, we’ll create a DefaultWsdl11Definition object. This exposes a standard WSDL 1.1 using an XsdSchema. The WSDL name will be the same as the bean name:

最后,我们将创建一个DefaultWsdl11Definition对象。这将使用 XsdSchema 公开一个标准的 WSDL 1.1。WSDL的名称将与Bean的名称相同。

@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
    DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
    wsdl11Definition.setPortTypeName("CountriesPort");
    wsdl11Definition.setLocationUri("/ws");
    wsdl11Definition.setTargetNamespace("http://www.baeldung.com/springsoap/gen");
    wsdl11Definition.setSchema(countriesSchema);
    return wsdl11Definition;
}

@Bean
public XsdSchema countriesSchema() {
    return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
}

5. Testing the SOAP Project

5.测试SOAP项目

Once the project configuration has been completed, we’re ready to test it.

一旦项目配置完成,我们就准备测试它。

5.1. Build and Run the Project

5.1.构建和运行该项目

It’s possible to create a WAR file and deploy it to an external application server. Instead, we’ll use Spring Boot, which is a faster and easier way to get the application up and running.

创建一个WAR文件并将其部署到外部应用服务器上是可行的。相反,我们将使用Spring Boot,这是一种更快、更简单的方式,可以让应用程序启动和运行。

First, we’ll add the following class to make the application executable:

首先,我们将添加以下类,使应用程序可执行。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Notice that we’re not using any XML files (like web.xml) to create this application. It’s all pure Java.

注意,我们没有使用任何XML文件(如web.xml)来创建这个应用程序。这都是纯Java。

Now we’re ready to build and run the application:

现在我们准备建立和运行应用程序。

mvn spring-boot:run

To check if the application is running properly, we can open the WSDL through the URL: http://localhost:8080/ws/countries.wsdl

为了检查应用程序是否正常运行,我们可以通过URL打开WSDL。http://localhost:8080/ws/countries.wsdl

5.2. Test a SOAP Request

5.2.测试一个SOAP请求

To test a request, we’ll create the following file and name it request.xml:

为了测试一个请求,我们将创建以下文件并将其命名为request.xml。

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:gs="http://www.baeldung.com/springsoap/gen">
    <soapenv:Header/>
    <soapenv:Body>
        <gs:getCountryRequest>
            <gs:name>Spain</gs:name>
        </gs:getCountryRequest>
    </soapenv:Body>
</soapenv:Envelope>

To send the request to our test server, we can use external tools, like SoapUI or the Google Chrome extension Wizdler. Another way is to run the following command in our shell:

为了向我们的测试服务器发送请求,我们可以使用外部工具,如SoapUI或谷歌浏览器扩展Wizdler。另一种方法是在我们的shell中运行以下命令。

curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws

The resulting response might not be easy to read without indentation or line breaks.

如果没有缩进或换行,所产生的响应可能不容易阅读。

To see it formatted, we can copy paste it to our IDE or another tool. If we’ve installed xmllib2, we can pipe the output of our curl command to xmllint:

为了看到它的格式化,我们可以把它复制粘贴到我们的IDE或其他工具。如果我们已经安装了xmllib2,我们可以用管道将curl命令的输出传给xmllint

curl [command-line-options] | xmllint --format -

The response should contain information about Spain:

回复中应包含有关西班牙的信息。

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
    <ns2:getCountryResponse xmlns:ns2="http://www.baeldung.com/springsoap/gen">
        <ns2:country>
            <ns2:name>Spain</ns2:name>
            <ns2:population>46704314</ns2:population>
            <ns2:capital>Madrid</ns2:capital>
            <ns2:currency>EUR</ns2:currency>
        </ns2:country>
    </ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

6. Conclusion

6.结语

In this article, we learned how to create a SOAP web service using Spring Boot. We also demonstrated how to generate Java code from an XSD file. Finally, we configured the Spring beans needed to process the SOAP requests.

在这篇文章中,我们学习了如何使用Spring Boot创建一个SOAP网络服务。我们还演示了如何从XSD文件生成Java代码。最后,我们配置了处理SOAP请求所需的Spring Bean。

The complete source code is available over on GitHub.

完整的源代码可以在GitHub上获得。