<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
生成 SOAP Web 服务
本指南将引导您完成使用 Spring 创建基于 SOAP 的 Web 服务服务器的过程。
您将构建什么
您将构建一个服务器,该服务器使用基于 WSDL 的 SOAP Web 服务公开来自各个欧洲国家的数据。
为了简化示例,您将对英国、西班牙和波兰使用硬编码数据。 |
您需要什么
-
大约 15 分钟
-
您喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
-
您也可以将代码直接导入到您的 IDE 中
如何完成本指南
与大多数 Spring 入门指南 一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会获得可工作的代码。
要**从头开始**,请继续执行从 Spring Initializr 开始。
要**跳过基础知识**,请执行以下操作
-
下载并解压缩本指南的源代码存储库,或使用Git克隆它:
git clone https://github.com/spring-guides/gs-soap-service.git
-
进入
gs-soap-service/initial
**完成后**,您可以针对gs-soap-service/complete
中的代码检查您的结果。
从 Spring Initializr 开始
您可以使用此预初始化项目,然后单击“生成”以下载 ZIP 文件。此项目配置为适合本教程中的示例。
要手动初始化项目
-
导航到https://start.spring.io。此服务会引入应用程序所需的所有依赖项,并为您完成大部分设置。
-
选择 Gradle 或 Maven 以及您要使用的语言。本指南假设您选择了 Java。
-
单击**依赖项**并选择**Spring Web**和**Spring Web Services**。
-
单击**生成**。
-
下载生成的 ZIP 文件,该文件是使用您的选择配置的 Web 应用程序的存档。
如果您的 IDE 集成了 Spring Initializr,则可以从您的 IDE 中完成此过程。 |
pom.xml 和build.gradle 文件都需要其他构建信息,您将在下一步中添加这些信息。 |
您也可以从 Github 分叉项目并在您的 IDE 或其他编辑器中打开它。 |
添加 Spring-WS 依赖项
该项目需要在您的构建文件中包含spring-ws-core
和wsdl4j
作为依赖项。
以下示例显示了如果您使用 Maven 需要对pom.xml
文件进行的更改
以下示例显示了如果您使用 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")
testImplementation('org.springframework.boot:spring-boot-starter-test')
}
创建 XML 架构以定义域
Web 服务域在 XML 架构文件 (XSD) 中定义,Spring-WS 会自动将其导出为 WSDL。
创建一个具有操作的 XSD 文件,以返回国家的name
、population
、capital
和currency
。以下列表(来自src/main/resources/countries.xsd
)显示了必要的 XSD 文件
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="https://springframework.org.cn/guides/gs-producing-web-service"
targetNamespace="https://springframework.org.cn/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>3.1.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 注释。这些标签使提取其中的部分内容到本指南中以进行更详细的解释变得更容易。您自己的构建文件中不需要这些注释。 |
下一步是添加genJaxb
任务,Gradle 使用该任务生成 Java 类。我们需要配置 gradle 以在build/generated-sources/jaxb
中找到这些生成的 Java 类,并将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 jakarta.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);
}
}
创建国家服务端点
要创建服务端点,您只需要一个 POJO,其中包含一些 Spring WS 注解来处理传入的 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 = "https://springframework.org.cn/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 用于根据消息的namespace
和localPart
选择处理程序方法。
@RequestPayload
注解指示传入的消息将映射到方法的request
参数。
@ResponsePayload
注解使 Spring WS 将返回值映射到响应有效负载。
在所有这些代码段中,除非您已运行基于 WSDL 生成域类的任务,否则io.spring.guides 类将在您的 IDE 中报告编译时错误。 |
配置 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("https://springframework.org.cn/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。 -
将此 Bean 命名为
messageDispatcherServlet
不会替换 Spring Boot 的默认DispatcherServlet
Bean。 -
DefaultMethodEndpointAdapter
配置注释驱动的 Spring WS 编程模型。这使得可以使用各种注解,例如@Endpoint
(前面已提到)。 -
DefaultWsdl11Definition
使用XsdSchema
公开标准 WSDL 1.1。
您需要为MessageDispatcherServlet 和DefaultWsdl11Definition 指定 Bean 名称。Bean 名称确定 Web 服务和生成的 WSDL 文件可用的 URL。在这种情况下,WSDL 将在http://<host>:<port>/ws/countries.wsdl 下可用。 |
此配置还使用 WSDL 位置 servlet 转换:servlet.setTransformWsdlLocations(true)
。如果您访问https://127.0.0.1:8080/ws/countries.wsdl,则soap:address
将具有正确的地址。如果您从分配给您的机器的公共面向 IP 地址访问 WSDL,则会看到该地址。
使应用程序可执行
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 Boot 基于类路径设置、其他 Bean 以及各种属性设置开始添加 Bean。例如,如果spring-webmvc
位于类路径中,则此注解将应用程序标记为 Web 应用程序,并激活关键行为,例如设置DispatcherServlet
。 -
@ComponentScan
:指示 Spring 在com/example
包中查找其他组件、配置和服务,从而找到控制器。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法启动应用程序。您是否注意到没有一行 XML 代码?也没有 web.xml
文件。此 Web 应用程序是 100% 纯 Java 编写的,您无需处理任何管道或基础设施的配置。
构建可执行 JAR 文件
您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必要依赖项、类和资源的单个可执行 JAR 文件,然后运行该文件。构建可执行 JAR 文件可以轻松地交付、版本化和部署服务作为应用程序,贯穿整个开发生命周期、跨越不同的环境等等。
如果您使用 Gradle,则可以使用 ./gradlew bootRun
运行应用程序。或者,您可以使用 ./gradlew build
构建 JAR 文件,然后运行 JAR 文件,如下所示:
如果您使用 Maven,则可以使用 ./mvnw spring-boot:run
运行应用程序。或者,您可以使用 ./mvnw clean package
构建 JAR 文件,然后运行 JAR 文件,如下所示:
此处描述的步骤创建了一个可运行的 JAR 文件。您还可以构建一个经典的 WAR 文件。 |
显示日志输出。服务应该在几秒钟内启动并运行。
测试应用程序
现在应用程序已启动并运行,您可以对其进行测试。创建一个名为 request.xml
的文件,其中包含以下 SOAP 请求:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="https://springframework.org.cn/guides/gs-producing-web-service">
<soapenv:Header/>
<soapenv:Body>
<gs:getCountryRequest>
<gs:name>Spain</gs:name>
</gs:getCountryRequest>
</soapenv:Body>
</soapenv:Envelope>
在测试 SOAP 接口时,有几个选项。您可以使用类似于 SoapUI 的工具,或者如果您在 *nix/Mac 系统上,则可以使用命令行工具。以下示例使用命令行中的 curl:
# Use data from file
curl --header "content-type: text/xml" -d @request.xml https://127.0.0.1:8080/ws
# Use inline XML data
curl <<-EOF -fsSL -H "content-type: text/xml" -d @- https://127.0.0.1:8080/ws \
> target/response.xml && xmllint --format target/response.xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="https://springframework.org.cn/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="https://springframework.org.cn/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 https://127.0.0.1:8080/ws > output.xml and xmllint --format output.xml 查看格式良好的结果。 |
总结
恭喜!您已经使用 Spring Web Services 开发了一个基于 SOAP 的服务。