使用Spring创建SOAP Web服务

2023/05/12

1. 概述

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

2. SOAP Web Services

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

SOAP是一种消息传递协议。消息(请求和响应)是基于HTTP的XML文档。XML协定由WSDL(Web Services Description Language)定义。它提供了一组规则来定义服务的消息、绑定、操作和位置。

SOAP中使用的XML可能变得极其复杂。出于这个原因,最好将SOAP与框架一起使用,例如JAX-WS或Spring,正如我们将在本教程中看到的那样。

3. 契约优先的开发方式

创建Web服务时有两种可能的方法:Contract-Last和Contract-First。当我们使用契约最后的方法时,我们从Java代码开始,并从类中生成Web服务契约(WSDL)。当使用契约优先时,我们从WSDL契约开始,从中生成Java类

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

4. 设置Spring Boot项目

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

4.1 Maven依赖项

让我们首先将spring-boot-starter-parent添加到我们的项目中:

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

接下来,让我们添加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 XSD文件

契约优先方法要求我们首先为我们的服务创建域(方法和参数)。我们将使用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.tuyucheng.com/springsoap/gen"
           targetNamespace="http://www.tuyucheng.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>

在这个文件中,我们可以看到getCountryRequest Web服务请求的格式。我们将其定义为接收一个字符串类型的参数。

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

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

4.3 生成域Java类

现在我们将从上一节中定义的XSD文件生成Java类。jaxb2-maven-plugin将在构建期间自动执行此操作。该插件使用XJC工具作为代码生成引擎。XJC将XSD模式文件编译成完全注解的Java类。

让我们在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>

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

  • <schemaDirectory>${project.basedir}/src/main/resources</schemaDirectory>:XSD文件的位置
  • <outputDirectory>${project.basedir}/src/main/java</outputDirectory>:我们希望将Java代码生成到哪里

要生成Java类,我们可以使用Java安装中的XJC工具。不过在我们的Maven项目中它甚至更简单,因为类将在通常的Maven构建期间自动生成

mvn compile

4.4 添加SOAP Web服务端点

SOAP Web服务端点类将处理所有传入的服务请求。它将启动处理,并发回响应。

在定义它之前,我们将创建一个CountryRepository以便为Web服务提供数据:

@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);
    }
}

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

@Endpoint
public class CountryEndpoint {

    private static final String NAMESPACE_URI = "http://www.tuyucheng.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;
    }
}

以下是一些需要注意的细节:

  • @Endpoint:将类注册为Spring WS作为Web服务端点
  • @ PayloadRoot:根据namespace和localPart属性定义处理程序方法
  • @ResponsePayload:指示此方法返回一个值以映射到响应负载
  • @RequestPayload:表示此方法接收要从传入请求映射的参数

4.5 SOAP Web服务配置Bean

现在让我们创建一个类来配置Spring消息调度程序Servlet以接收请求:

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

@EnableWs在此Spring Boot应用程序中启用SOAP Web Service功能。WebServiceConfig类扩展了WsConfigurerAdapter基类,该基类配置了注解驱动的Spring-WS编程模型。

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

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

我们将设置Servlet的注入ApplicationContext对象,以便Spring-WS可以找到其他Spring beans。

我们还将启用WSDL位置Servlet转换。这会转换WSDL中soap:address的location属性,以便它反映传入请求的URL。

最后,我们将创建一个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.tuyucheng.com/springsoap/gen");
    wsdl11Definition.setSchema(countriesSchema);
    return wsdl11Definition;
}

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

5. 测试SOAP项目

项目配置完成后,我们就可以对其进行测试了。

5.1 构建并运行项目

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

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

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

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

现在我们准备构建和运行应用程序:

mvn spring-boot:run

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

5.2 测试SOAP请求

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

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

要将请求发送到我们的测试服务器,我们可以使用外部工具,如SoapUI或Google Chrome扩展程序Wizdler。另一种方法是在我们的shell中运行以下命令:

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

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

要查看它的格式,我们可以将其复制粘贴到我们的IDE或其他工具中。如果我们已经安装了xmllib2,我们可以将curl命令的输出通过管道传递给xmllint:

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

响应应包含有关西班牙的信息:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns2:getCountryResponse xmlns:ns2="http://www.tuyucheng.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. 总结

在本文中,我们学习了如何使用Spring Boot创建SOAP Web服务。我们还演示了如何从XSD文件生成Java代码。最后,我们配置了处理SOAP请求所需的Spring bean。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章