<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
消费 SOAP Web 服务
本指南将引导您使用 Spring 消费基于 SOAP 的 Web 服务。
您将构建什么
您需要什么
-
大约 15 分钟
-
您喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
-
您也可以直接将代码导入您的 IDE
如何完成本指南
与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,或者跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可工作的代码。
要从头开始,请继续阅读使用 Spring Initializr 开始。
要跳过基础部分,请执行以下操作
-
下载并解压本指南的源代码仓库,或使用 Git 克隆:
git clone https://github.com/spring-guides/gs-consuming-web-service.git
-
cd 进入
gs-consuming-web-service/initial
完成后,您可以对照 gs-consuming-web-service/complete
中的代码检查您的结果。
在本地运行目标 Web 服务
按照 配套指南 中的步骤操作,或克隆 仓库 并从其 complete
目录运行服务(例如,使用 mvn spring-boot:run
)。您可以通过在浏览器中访问 http://localhost:8080/ws/countries.wsdl
来验证它是否正常工作。如果您不这样做,稍后您会在构建中看到 JAXB 工具报告的令人困惑的异常。
使用 Spring Initializr 开始
对于所有 Spring 应用,您都应该从 Spring Initializr 开始。Initializr 提供了一种快速方式来引入应用程序所需的所有依赖项,并为您完成大量设置工作。此示例只需要 Spring Web Services 依赖项。
您可以使用这个预初始化项目,然后点击 Generate 下载 ZIP 文件。此项目已配置好,适用于本教程中的示例。
初始化项目
-
导航到 https://start.spring.io。该服务会引入应用程序所需的所有依赖项,并为您完成大部分设置。
-
选择 Gradle 或 Maven 以及您想使用的语言。本指南假设您选择了 Java。
-
点击 Dependencies 并选择 Spring Web Services。
-
点击 Generate。
-
下载生成的 ZIP 文件,这是一个根据您的选择配置的 Web 应用程序归档文件。
如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成此过程。 |
您也可以从 Github fork 项目并在您的 IDE 或其他编辑器中打开它。 |
修改构建文件
Spring Initializr 创建的构建文件需要为本指南做相当多的修改。此外,对 pom.xml
(Maven) 和 build.gradle
(Gradle) 的修改差异很大。
Maven
对于 Maven,您需要添加一个依赖项、一个 profile 和一个 WSDL 生成插件。
以下列表显示了您需要在 Maven 中添加的依赖项
根据 WSDL 生成领域对象 部分描述了 WSDL 生成插件。
以下列表显示了最终的 pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>consuming-web-service-complete</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consuming-web-service-complete</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <!-- tag::dependency[] --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- end::dependency[] --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- tag::wsdl[] --> <plugin> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>3.0.0</version> <executions> <execution> <goals> <goal>wsimport</goal> </goals> </execution> </executions> <configuration> <packageName>com.example.consumingwebservice.wsdl</packageName> <wsdlUrls> <wsdlUrl>http://localhost:8080/ws/countries.wsdl</wsdlUrl> </wsdlUrls> <sourceDestDir>${sourcesDir}</sourceDestDir> <destDir>${classesDir}</destDir> <extension>true</extension> </configuration> </plugin> <!-- end::wsdl[] --> </plugins> </build> </project>
Gradle
对于 Gradle,您需要添加一个依赖项、一个配置、一个 bootJar
部分和一个 WSDL 生成插件。
以下列表显示了您需要在 Gradle 中添加的依赖项
implementation ('org.springframework.boot:spring-boot-starter-web-services') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
jaxws 'com.sun.xml.ws:jaxws-tools:3.0.0',
'jakarta.xml.ws:jakarta.xml.ws-api:3.0.0',
'jakarta.xml.bind:jakarta.xml.bind-api:3.0.0',
'jakarta.activation:jakarta.activation-api:2.0.0',
'com.sun.xml.ws:jaxws-rt:3.0.0'
注意排除 Tomcat。如果允许 Tomcat 在此构建中运行,则会与提供国家数据的 Tomcat 实例发生端口冲突。
由于端口冲突,初始项目无法启动。您可以通过添加一个包含 server.port=8081 单一属性的 application.properties 文件来解决此问题。由于初始项目是作为一个起点存在的,您可以跳过尝试运行它的步骤。 |
根据 WSDL 生成领域对象 部分描述了 WSDL 生成插件。
以下列表显示了最终的 build.gradle
文件
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.0'
id 'io.spring.dependency-management' version '1.1.5'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
ext.jaxwsSourceDir = "${buildDir}/generated/sources/jaxws"
// tag::configurations[]
configurations {
jaxws
}
// end::configurations[]
repositories {
mavenCentral()
}
// tag::wsdl[]
task wsimport {
description = 'Generate classes from wsdl using wsimport'
doLast {
project.mkdir(jaxwsSourceDir)
ant {
taskdef(name: 'wsimport',
classname: 'com.sun.tools.ws.ant.WsImport',
classpath: configurations.jaxws.asPath
)
wsimport(
keep: true,
destdir: jaxwsSourceDir,
extension: "true",
verbose: true,
wsdl: "http://localhost:8080/ws/countries.wsdl",
xnocompile: true,
package: "com.example.consumingwebservice.wsdl") {
xjcarg(value: "-XautoNameResolution")
}
}
}
}
sourceSets {
main {
java.srcDirs += jaxwsSourceDir
}
}
compileJava {
dependsOn wsimport
}
// end::wsdl[]
dependencies {
// tag::dependency[]
implementation ('org.springframework.boot:spring-boot-starter-web-services') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
jaxws 'com.sun.xml.ws:jaxws-tools:3.0.0',
'jakarta.xml.ws:jakarta.xml.ws-api:3.0.0',
'jakarta.xml.bind:jakarta.xml.bind-api:3.0.0',
'jakarta.activation:jakarta.activation-api:2.0.0',
'com.sun.xml.ws:jaxws-rt:3.0.0'
// end::dependency[]
testImplementation('org.springframework.boot:spring-boot-starter-test')
}
tasks.named('test') {
useJUnitPlatform()
}
根据 WSDL 生成领域对象
SOAP Web 服务的接口在 WSDL 中定义。JAXB 提供了一种从 WSDL(或者更确切地说,是 WSDL 的 <Types/>
部分中包含的 XSD)生成 Java 类的方式。您可以在 http://localhost:8080/ws/countries.wsdl
找到国家服务的 WSDL。
要在 Maven 中从 WSDL 生成 Java 类,您需要以下插件配置
<plugin>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
</execution>
</executions>
<configuration>
<packageName>com.example.consumingwebservice.wsdl</packageName>
<wsdlUrls>
<wsdlUrl>http://localhost:8080/ws/countries.wsdl</wsdlUrl>
</wsdlUrls>
<sourceDestDir>${sourcesDir}</sourceDestDir>
<destDir>${classesDir}</destDir>
<extension>true</extension>
</configuration>
</plugin>
此配置会为指定 URL 找到的 WSDL 生成类,并将这些类放在 com.example.consumingwebservice.wsdl
包中。要生成该代码,请运行 ./mvnw compile
,然后如果您想检查是否成功,请查看 target/generated-sources
。
要使用 Gradle 完成相同的操作,您需要在构建文件中添加以下内容
task wsimport {
description = 'Generate classes from wsdl using wsimport'
doLast {
project.mkdir(jaxwsSourceDir)
ant {
taskdef(name: 'wsimport',
classname: 'com.sun.tools.ws.ant.WsImport',
classpath: configurations.jaxws.asPath
)
wsimport(
keep: true,
destdir: jaxwsSourceDir,
extension: "true",
verbose: true,
wsdl: "http://localhost:8080/ws/countries.wsdl",
xnocompile: true,
package: "com.example.consumingwebservice.wsdl") {
xjcarg(value: "-XautoNameResolution")
}
}
}
}
sourceSets {
main {
java.srcDirs += jaxwsSourceDir
}
}
compileJava {
dependsOn wsimport
}
由于 Gradle (目前)没有 JAXB 插件,因此它涉及一个 Ant 任务,这使其比 Maven 更复杂一些。要生成该代码,请运行 ./gradlew compileJava
,然后如果您想检查是否成功,请查看 build/generated-sources
。
在 Maven 和 Gradle 中,JAXB 领域对象生成过程都已集成到构建工具的生命周期中,因此一旦构建成功,您无需运行任何额外步骤。
创建国家服务客户端
要创建 Web 服务客户端,您必须扩展 WebServiceGatewaySupport
类并编写您的操作代码,如下面的示例(来自 src/main/java/com/example/consumingwebservice/CountryClient.java
)所示
package com.example.consumingwebservice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.client.core.SoapActionCallback;
import com.example.consumingwebservice.wsdl.GetCountryRequest;
import com.example.consumingwebservice.wsdl.GetCountryResponse;
public class CountryClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(CountryClient.class);
public GetCountryResponse getCountry(String country) {
GetCountryRequest request = new GetCountryRequest();
request.setName(country);
log.info("Requesting location for " + country);
GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate()
.marshalSendAndReceive("http://localhost:8080/ws/countries", request,
new SoapActionCallback(
"https://springframework.org.cn/guides/gs-producing-web-service/GetCountryRequest"));
return response;
}
}
客户端包含一个方法(getCountry
),用于执行实际的 SOAP 交换。
在此方法中,GetCountryRequest
和 GetCountryResponse
类都派生自 WSDL,并在 JAXB 生成过程中生成(如根据 WSDL 生成领域对象中所述)。它创建 GetCountryRequest
请求对象,并使用 country
参数(国家名称)对其进行设置。在打印出国家名称后,它使用 WebServiceGatewaySupport
基类提供的 WebServiceTemplate
执行实际的 SOAP 交换。它传递 GetCountryRequest
请求对象(以及一个 SoapActionCallback
以便在请求中传递 SOAPAction 头部),因为 WSDL 中描述了在 <soap:operation/>
元素中需要此头部。它将响应转换为 GetCountryResponse
对象,然后返回该对象。
配置 Web 服务组件
Spring WS 使用 Spring Framework 的 OXM 模块,该模块包含 Jaxb2Marshaller
用于序列化和反序列化 XML 请求,如下面的示例(来自 src/main/java/com/example/consumingwebservice/CountryConfiguration.java
)所示
package com.example.consumingwebservice;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
@Configuration
public class CountryConfiguration {
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in
// pom.xml
marshaller.setContextPath("com.example.consumingwebservice.wsdl");
return marshaller;
}
@Bean
public CountryClient countryClient(Jaxb2Marshaller marshaller) {
CountryClient client = new CountryClient();
client.setDefaultUri("http://localhost:8080/ws");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
marshaller
指向生成的领域对象集合,并将使用它们在 XML 和 POJO 之间进行序列化和反序列化。
countryClient
已创建并配置了前面显示的国家服务 URI。它也配置为使用 JAXB marshaller。
运行应用程序
此应用程序已打包,可以从控制台运行并检索给定国家名称的数据,如下面的列表(来自 src/main/java/com/example/consumingwebservice/ConsumingWebServiceApplication.java
)所示
package com.example.consumingwebservice;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.example.consumingwebservice.wsdl.GetCountryResponse;
@SpringBootApplication
public class ConsumingWebServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumingWebServiceApplication.class, args);
}
@Bean
CommandLineRunner lookup(CountryClient countryClient) {
return args -> {
String country = "Spain";
if (args.length > 0) {
country = args[0];
}
GetCountryResponse response = countryClient.getCountry(country);
System.err.println(response.getCountry().getCurrency());
};
}
}
main()
方法委托给 SpringApplication
帮助类,将 CountryConfiguration.class
作为参数传递给其 run()
方法。这告诉 Spring 读取 CountryConfiguration
中的注解元数据,并将其作为 Spring 应用上下文中的一个组件来管理。
此应用程序硬编码为查找“Spain”。在本指南稍后,您将看到如何在不编辑代码的情况下输入不同的符号。 |
构建可执行 JAR
您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必要的依赖项、类和资源的可执行 JAR 文件并运行它。构建可执行 JAR 使在整个开发生命周期、跨不同环境等过程中,将服务作为应用程序进行分发、版本控制和部署变得容易。
如果您使用 Gradle,您可以使用 ./gradlew bootRun
运行应用程序。或者,您可以使用 ./gradlew build
构建 JAR 文件,然后按如下方式运行 JAR 文件
如果您使用 Maven,您可以使用 ./mvnw spring-boot:run
运行应用程序。或者,您可以使用 ./mvnw clean package
构建 JAR 文件,然后按如下方式运行 JAR 文件
此处描述的步骤会创建一个可运行的 JAR 文件。您也可以构建一个经典的 WAR 文件。 |
将显示日志输出。服务应该会在几秒钟内启动并运行。
以下列表显示了初始响应
Requesting country data for Spain
<getCountryRequest><name>Spain</name>...</getCountryRequest>
您可以通过运行以下命令插入不同的国家
java -jar build/libs/gs-consuming-web-service-0.1.0.jar Poland
然后响应更改为以下内容
Requesting location for Poland
<getCountryRequest><name>Poland</name>...</getCountryRequest>
总结
恭喜!您刚刚开发了一个使用 Spring 消费基于 SOAP 的 Web 服务的客户端。