使用 Spring 创建基于 SOAP 的 Web 服务服务器的过程

时间:2022-12-23 16:57:48

使用 Spring 创建基于 SOAP 的 Web 服务服务器的过程

本指南将引导您完成使用 Spring 创建基于 SOAP 的 Web 服务服务器的过程。

您将构建什么

您将构建一个服务器,该服务器使用基于 WSDL 的 SOAP Web 服务公开来自不同欧洲国家的数据。

为了简化示例,您将使用英国、西班牙和波兰的硬编码数据。

你需要什么

  • 约15分钟
  • 最喜欢的文本编辑器或 IDE
  • JDK 1.8或以后
  • 格拉德尔 4+​或梅文 3.2+
  • 您也可以将代码直接导入到 IDE 中:
  • 弹簧工具套件 (STS)
  • 智能理念
  • VSCode

如何完成本指南

像大多数春天一样入门指南,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。

要从头开始,请继续从 Spring 初始化开始.

要跳过基础知识,请执行以下操作:

  • 下载​并解压缩本指南的源存储库,或使用吉特:git clone https://github.com/spring-guides/gs-soap-service.git
  • 光盘成gs-soap-service/initial
  • 跳转到添加 Spring-WS 依赖项.

完成后,您可以根据 中的代码检查结果。​​gs-soap-service/complete​

从 Spring 初始化开始

你可以使用这个预初始化项目,然后单击生成以下载 ZIP 文件。此项目配置为适合本教程中的示例。

手动初始化项目:

  1. 导航到https://start.spring.io.此服务拉入应用程序所需的所有依赖项,并为您完成大部分设置。
  2. 选择 Gradle 或 Maven 以及您要使用的语言。本指南假定您选择了 Java。
  3. 单击“依赖关系”,然后选择“Spring Web”和“Spring Web Services”。
  4. 单击生成
  5. 下载生成的 ZIP 文件,该文件是配置了您选择的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。

和 文件都需要其他生成信息,您将在下一步中添加这些信息。​​pom.xml​​​​build.gradle​

您也可以从 Github 分叉项目,然后在 IDE 或其他编辑器中打开它。

添加 Spring-WS 依赖项

项目需要在生成文件中包含 和 作为依赖项。​​spring-ws-core​​​​wsdl4j​

以下示例显示了使用 Maven 时需要对文件进行的更改:​​pom.xml​

<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>

以下示例显示了在使用 Gradle 时需要对文件进行的更改:​​build.gradle​

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-web-services'
implementation 'wsdl4j:wsdl4j'
jaxb("org.glassfish.jaxb:jaxb-xjc")
// For Java 11:
implementation 'org.glassfish.jaxb:jaxb-runtime'
testImplementation('org.springframework.boot:spring-boot-starter-test')
}

创建 XML 架构以定义域

Web 服务域是在 XML 模式文件 (XSD) 中定义的,Spring-WS 将自动导出为 WSDL。

创建一个 XSD 文件,其中包含返回国家/地区 、 和 的操作。以下清单(来自 )显示了必要的 XSD 文件:​​name​​​​population​​​​capital​​​​currency​​​​src/main/resources/countries.xsd​

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

下一步是从 XSD 文件生成 Java 类。正确的方法是在构建期间使用 Maven 或 Gradle 插件自动执行此操作。

以下清单显示了 Maven 所需的插件配置:

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

生成的类放置在目录中。​​target/generated-sources/jaxb/​

要对 Gradle 执行相同的操作,您首先需要在构建文件中配置 JAXB,如以下清单所示:

configurations {
jaxb
}

bootJar {
archiveBaseName = 'gs-producing-web-service'
archiveVersion = '0.1.0'
}

构建文件具有和注释。这些标签可以更轻松地将其片段提取到本指南中,以获得更详细的解释。您自己的构建文件中不需要这些注释。​​tag​​​​end​

下一步是添加任务,Gradle 使用该任务生成 Java 类。我们需要配置 gradle 以查找这些生成的 Java 类并添加为任务的依赖项。以下清单显示了必要的添加:​​genJaxb​​​​build/generated-sources/jaxb​​​​genJaxb​​​​compileJava​

sourceSets {
main {
java {
srcDir 'src/main/java'
srcDir 'build/generated-sources/jaxb'
}
}
}

task genJaxb {
ext.sourcesDir = "${buildDir}/generated-sources/jaxb"
ext.schema = "src/main/resources/countries.xsd"

outputs.dir sourcesDir

doLast() {
project.ant {
taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
classpath: configurations.jaxb.asPath
mkdir(dir: sourcesDir)

xjc(destdir: sourcesDir, schema: schema) {
arg(value: "-wsdl")
produces(dir: sourcesDir, includes: "**/*.java")
}
}
}
}

compileJava.dependsOn genJaxb

因为 Gradle 还没有 JAXB 插件,它涉及一个 Ant 任务,这使得它比 Maven 更复杂。

在这两种情况下,JAXB 域对象生成过程都已连接到构建工具的生命周期中,因此无需运行额外的步骤。

创建国家/地区存储库

为了向 Web 服务提供数据,请创建一个国家/地区存储库。在本指南中,您将使用硬编码数据创建虚拟国家/地区存储库实现。以下清单(来自 )显示了如何执行此操作:​​src/main/java/com/example/producingwebservice/CountryRepository.java​

package com.example.producingwebservice;

import javax.annotation.PostConstruct;
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<>();

@PostConstruct
public void initData() {
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);
}
}

创建国家/地区服务终结点

要创建服务端点,你只需要一个带有一些 Spring WS 注释的 POJO 来处理传入的 SOAP 请求。下面的清单(来自)显示了这样的类:​​src/main/java/com/example/producingwebservice/CountryEndpoint.java​

package com.example.producingwebservice;

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

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

@Endpoint
public class CountryEndpoint {
private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";

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 作为处理传入 SOAP 消息的潜在候选者。

这@PayloadRoot然后,Spring WS 使用注释根据消息的 and 来选择处理程序方法。namespacelocalPart

这@RequestPayload批注指示传入消息将映射到方法的参数。request

这@ResponsePayload注释使 Spring WS 将返回值映射到响应有效负载。

在所有这些代码块中,这些类将在 IDE 中报告编译时错误,除非您已运行任务以基于 WSDL 生成域类。​​io.spring.guides​

配置 Web 服务 Bean

使用 Spring WS 相关的 bean 配置创建一个新类,如以下清单(来自)所示:​​src/main/java/com/example/producingwebservice/WebServiceConfig.java​

package com.example.producingwebservice;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}

@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}

@Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
}
}
  • Spring WS 使用不同的 servlet 类型来处理 SOAP 消息:MessageDispatcherServlet.注射和凝固很重要ApplicationContext自MessageDispatcherServlet.如果没有它,Spring WS 将不会自动检测 Spring bean。
  • 命名此豆子并不能取代Spring Boot的messageDispatcherServlet默认 BeanDispatcherServlet.
  • DefaultMethodEndpointAdapter配置注释驱动的 Spring WS 编程模型。这使得可以使用各种注释,例如@Endpoint(前面提到过)。
  • DefaultWsdl11Definition通过使用公开标准 WSDL 1.1XsdSchema

您需要为 指定 Bean 名称MessageDispatcherServlet和DefaultWsdl11Definition​.Bean 名称确定 Web 服务和生成的 WSDL 文件可用的 URL。在这种情况下,WSDL 将在 下可用。​​http://<host>:<port>/ws/countries.wsdl​

此配置还使用 WSDL 位置 servlet 转换:。如果您访问​​servlet.setTransformWsdlLocations(true)​​​​http://localhost:8080/ws/countries.wsdl​​,将具有正确的地址。如果改为从分配给计算机的面向公众的 IP 地址访问 WSDL,则会看到该地址。​​soap:address​

使应用程序可执行

Spring Boot 会为你创建一个应用程序类。在这种情况下,它不需要进一步修改。您可以使用它来运行此应用程序。以下清单(来自 )显示了应用程序类:​​src/main/java/com/example/producingwebservice/ProducingWebServiceApplication.java​

package com.example.producingwebservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProducingWebServiceApplication {

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

​@SpringBootApplication​​是一个方便的注释,它添加了以下所有内容:

  • ​@Configuration​​:将类标记为应用程序上下文的 Bean 定义源。
  • ​@EnableAutoConfiguration​​:告诉 Spring 引导根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果 在类路径上,则此注释会将应用程序标记为 Web 应用程序并激活关键行为,例如设置 .spring-webmvcDispatcherServlet
  • ​@ComponentScan​​:告诉 Spring 在包中查找其他组件、配置和服务,让它找到控制器。com/example

该方法使用 Spring Boot 的方法启动应用程序。您是否注意到没有一行 XML?也没有文件。此 Web 应用程序是 100% 纯 Java,您无需处理配置任何管道或基础结构。​​main()​​​​SpringApplication.run()​​​​web.xml​

构建可执行的 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必需依赖项、类和资源的可执行 JAR 文件并运行该文件。通过构建可执行 jar,可以轻松地在整个开发生命周期中跨不同环境等将服务作为应用程序进行交付、版本控制和部署。

如果使用 Gradle,则可以使用 .或者,您可以使用 JAR 文件生成 JAR 文件,然后运行该文件,如下所示:​​./gradlew bootRun​​​​./gradlew build​

java -jar build/libs/gs-soap-service-0.1.0.jar

如果使用 Maven,则可以使用 运行应用程序。或者,您可以使用 JAR 文件生成 JAR 文件,然后运行该文件,如下所示:​​./mvnw spring-boot:run​​​​./mvnw clean package​

java -jar target/gs-soap-service-0.1.0.jar

此处描述的步骤将创建一个可运行的 JAR。你也可以构建经典 WAR 文件.

将显示日志记录输出。该服务应在几秒钟内启动并运行。

测试应用程序

现在应用程序正在运行,您可以对其进行测试。创建一个包含以下 SOAP 请求的调用文件:​​request.xml​

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="http://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 接口时,有几个选项。您可以使用类似于肥皂用户界面或者如果您使用的是 *nix/Mac 系统,请使用命令行工具。以下示例在命令行中使用 curl:

# Use data from file
curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws
# Use inline XML data
curl <<-EOF -fsSL -H "content-type: text/xml" -d @- http://localhost:8080/ws \
> target/response.xml && xmllint --format target/response.xml

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

EOF

因此,您应该看到以下响应:

<?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="http://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>

输出很可能是一个紧凑的XML文档,而不是上面显示的格式良好的文档。如果您的系统上安装了 xmllib2,您可以看到结果格式很好。​​curl -fsSL --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws > output.xml and xmllint --format output.xml​

总结

祝贺!您已经使用 Spring Web Services 开发了一个基于 SOAP 的服务。