创建Spring SOAP Web服务

本指南介绍如何使用Spring创建基于SOAP的Web服务服务器。

将构建什么

你将构建一个服务器,通过基于WSDL的SOAP Web服务公开欧洲国家的数据。

为简化示例,将英国、西班牙和波兰的数据硬编码在程序中。

准备条件

如何完成本指南

与大多数Spring入门指南一样,可以从零逐步完成,也可以跳过已熟悉的基础设置,最终得到可工作的代码。

从零开始时,请进入使用Spring Initializr一节。

要跳过基础设置,请执行:

完成后,可将结果与gs-soap-service/complete中的代码比较。

使用Spring Initializr

可以打开预配置项目,点击Generate下载ZIP。该项目已经按本教程示例完成配置。

手动初始化项目:

  1. 进入https://start.spring.io。该服务会引入应用所需依赖,并完成大部分初始设置。

  2. 选择Gradle或Maven,以及使用的语言。本指南假定选择Java。

  3. 点击Dependencies,选择Spring Web Services。

  4. 点击Generate。

  5. 下载生成的ZIP,其中是按照所选配置创建的Web应用。

如果IDE集成了Spring Initializr,也可以在IDE中完成这些步骤。

添加WSDL运行时依赖

服务在运行时需要wsdl4j,将它添加到Gradle构建中:

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-webservices'
	runtimeOnly 'wsdl4j:wsdl4j'
	testImplementation 'org.springframework.boot:spring-boot-starter-webservices-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

或者添加到POM文件:

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-webservices</artifactId>
	</dependency>
	<dependency>
		<groupId>wsdl4j</groupId>
		<artifactId>wsdl4j</artifactId>
		<optional>true</optional>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-webservices-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

创建定义领域的XML Schema

Web服务的领域通过XML Schema文件(XSD)定义,Spring-WS会自动将其导出为WSDL。

创建XSD,定义返回国家名称、人口、首都和货币的操作。以下内容来自src/main/resources/META-INF/schemas/countriesWs.xsd:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="https://spring.io/guides/gs-producing-web-service"
           targetNamespace="https://spring.io/guides/gs-producing-web-service" 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>

根据XML Schema生成领域类

下一步根据XSD生成Java类。合适的方式是在构建期间,通过Maven或Gradle插件自动生成。

Maven所需插件配置如下:

<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>jaxb2-maven-plugin</artifactId>
	<version>4.0.0</version>
	<executions>
		<execution>
			<id>xjc</id>
			<goals>
				<goal>xjc</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<sources>
			<source>${project.basedir}/src/main/resources/META-INF/schemas/countriesWs.xsd</source>
		</sources>
	</configuration>
</plugin>

生成类位于target/generated-sources/jaxb/目录。

使用Gradle时,在插件区域声明新插件:

id 'com.github.bjornvester.xjc' version '1.9.1'

然后将其配置为指向XSD文件所在位置:

xjc {
	xsdDir.set(layout.projectDirectory.dir("src/main/resources/META-INF/schemas"))
}

两种方式都会把JAXB领域对象生成过程接入构建工具生命周期,无需额外运行步骤。

创建国家数据仓库

为了向Web服务提供数据,需要创建国家数据仓库。本指南使用硬编码数据构建一个模拟实现。以下代码来自src/main/java/com/example/producingwebservice/CountryRepository.java:

package com.example.producingwebservice;

import java.util.HashMap;
import java.util.Map;

import io.spring.guides.gs_producing_web_service.Country;
import io.spring.guides.gs_producing_web_service.Currency;

import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

@Component
public class CountryRepository {

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

	static {
		Country spain = new Country();
		spain.setName("Spain");
		spain.setCapital("Madrid");
		spain.setCurrency(Currency.EUR);
		spain.setPopulation(46704314);

		countries.put(spain.getName(), spain);

		Country poland = new Country();
		poland.setName("Poland");
		poland.setCapital("Warsaw");
		poland.setCurrency(Currency.PLN);
		poland.setPopulation(38186860);

		countries.put(poland.getName(), poland);

		Country uk = new Country();
		uk.setName("United Kingdom");
		uk.setCapital("London");
		uk.setCurrency(Currency.GBP);
		uk.setPopulation(63705000);

		countries.put(uk.getName(), uk);
	}

	public Country findCountry(String name) {
		Assert.notNull(name, "The country's name must not be null");
		return countries.get(name);
	}
}

创建国家服务端点

只需在普通Java对象(POJO)上添加少量Spring WS注解,就可以创建处理SOAP请求的服务端点。以下类来自src/main/java/com/example/producingwebservice/CountryEndpoint.java:

package com.example.producingwebservice;

import io.spring.guides.gs_producing_web_service.GetCountryRequest;
import io.spring.guides.gs_producing_web_service.GetCountryResponse;

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 org.springframework.ws.server.endpoint.annotation.ResponsePayload;

@Endpoint
public class CountryEndpoint {

	private static final String NAMESPACE_URI = "https://spring.io/guides/gs-producing-web-service";

	private final CountryRepository countryRepository;

	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处理入站SOAP消息的候选者。

Spring WS根据消息的命名空间与localPart,使用@PayloadRoot选择处理方法。

@RequestPayload表示将入站消息映射到方法的请求参数。

@ResponsePayload让Spring WS把方法返回值映射为响应负载。

在这些代码中,若尚未运行生成领域类的任务,IDE会报告io.spring.guides类的编译错误。原文在这里将生成依据称为WSDL;本指南前面的生成配置实际使用XSD。

公开Web服务

现在公开服务,让客户端能够获取WSDL定义。Spring Boot会扫描配置位置中的所有.xsd和.wsdl文件。首先编辑src/main/resources/application.properties,声明Schema文件位置:

spring.webservices.wsdl-locations=classpath:META-INF/schemas/

Spring Boot会创建名称与文件名相同的SimpleXsdSchema或DefaultWsdl11Definition Bean。这里会得到名为countriesWs的SimpleXsdSchema Bean,可用它创建对外提供WSDL的Bean。创建src/main/java/com/example/producingwebservice/WebServiceConfig.java,内容如下:

package com.example.producingwebservice;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;

@Configuration(proxyBeanMethods = false)
public class WebServiceConfig {

	@Bean
	public DefaultWsdl11Definition countries(SimpleXsdSchema countriesWs) {
		DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
		wsdl11Definition.setPortTypeName("CountriesPort");
		wsdl11Definition.setLocationUri("/services");
		wsdl11Definition.setTargetNamespace("https://spring.io/guides/gs-producing-web-service");
		wsdl11Definition.setSchema(countriesWs);
		return wsdl11Definition;
	}

}

DefaultWsdl11Definition使用已配置的XsdSchema提供标准WSDL 1.1定义。

DefaultWsdl11Definition的Bean名称决定生成的WSDL文件URL。本例为http://<host>:<port>/services/countries.wsdl,其中主机和端口是待替换的占位符。

默认通过/services HTTP端点公开服务,也可以通过spring.webservices.path属性定制。

测试应用

应用运行后即可测试。创建request.xml,包含以下SOAP请求:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
				  xmlns:gs="https://spring.io/guides/gs-producing-web-service">
   <soapenv:Header/>
   <soapenv:Body>
      <gs:getCountryRequest>
         <gs:name>Spain</gs:name>
      </gs:getCountryRequest>
   </soapenv:Body>
</soapenv:Envelope>

测试SOAP接口有几种方式。可以使用SoapUI之类的工具,或在类Unix、Mac系统上使用命令行工具。下面使用curl:

# Use data from file
$ curl --header "Content-Type: text/xml" -d @request.xml http://localhost:8080/services | xmllint --format

预期响应如下:

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <SOAP-ENV:Body>
    <ns2:getCountryResponse xmlns:ns2="https://spring.io/guides/gs-producing-web-service">
      <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>

总结

至此,已完成使用Spring Web Services构建SOAP服务的步骤。

另请参阅

以下指南也可能有帮助:

要编写新指南或改进现有指南,可查阅贡献指南。

所有指南的代码采用Apache License 2.0(ASLv2),文字采用知识共享署名—禁止演绎许可。相应版权与许可声明继续适用。


原文:创建Spring SOAP Web服务;作者:Spring Guides贡献者。版本或日期:官方原文抓取版本。原文及源码权利归相应权利人所有。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容