ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 消费SOAP Web服务 本指南将引导您完成使用Spring使用基于SOAP的Web服务的过程。 ## 你会建立什么 您将构建一个客户端,该客户端使用 从基于WSDL的远程Web服务中获取国家/地区数据数据 [SOAP](https://en.wikipedia.org/wiki/SOAP) 。 您可以按照 查找有关国家/地区服务的更多信息并自己运行该服务 [本指南](https://spring.io/guides/gs/producing-web-service/) 。 该服务提供国家/地区数据。 您将能够根据国家名称查询有关国家的数据。 ## 你需要什么 * 约15分钟 * 最喜欢的文本编辑器或IDE * [JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 或更高版本 * [Gradle 4+](http://www.gradle.org/downloads) 或 [Maven 3.2+](https://maven.apache.org/download.cgi) * 您还可以将代码直接导入到IDE中: * [弹簧工具套件(STS)](https://spring.io/guides/gs/sts) * [IntelliJ IDEA](https://spring.io/guides/gs/intellij-idea/) ## 如何完成本指南 像大多数Spring 一样 [入门指南](https://spring.io/guides) ,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。 无论哪种方式,您最终都可以使用代码。 要 **从头开始** ,请继续进行“ [从Spring Initializr开始”](https://spring.io/guides/gs/consuming-web-service/#scratch) 。 要 **跳过基础知识** ,请执行以下操作: * [下载](https://github.com/spring-guides/gs-consuming-web-service/archive/master.zip) 并解压缩本指南的源存储库,或使用 对其进行克隆 [Git](https://spring.io/understanding/Git) : `git clone [https://github.com/spring-guides/gs-consuming-web-service.git](https://github.com/spring-guides/gs-consuming-web-service.git)` * 光盘进入 `gs-consuming-web-service/initial` * 前进以 [基于WSDL生成域对象](https://spring.io/guides/gs/consuming-web-service/#initial) 。 **完成后** ,您可以根据中的代码检查结果 `gs-consuming-web-service/complete`. 如果阅读了《 生产SOAP Web服务》 ,您可能会想知道为什么本指南不使用 spring-boot-starter-ws? 该Spring Boot启动器仅适用于服务器端Web服务。 该入门程序可以带入嵌入式Tomcat等功能,而无需进行Web调用。 ## 在本地运行目标Web服务 请遵循 的步骤, [随附指南中](https://spring.io/guides/gs/producing-web-service/) 或克隆 [存储库](https://github.com/spring-guides/gs-producing-web-service) 并运行服务(例如,通过使用 `mvn spring-boot:run`)从 `complete`目录。 您可以通过访问来验证它是否有效 `[http://localhost:8080/ws/countries.wsdl](http://localhost:8080/ws/countries.wsdl)`在您的浏览器中。 如果您不这样做,您稍后会在JAXB工具中看到一个令人困惑的异常。 ## 从Spring Initializr开始 对于所有Spring应用程序,您应该从 开始 [Spring Initializr](https://start.spring.io) 。 Initializr提供了一种快速的方法来提取应用程序所需的所有依赖项,并为您完成了许多设置。 该示例仅需要Spring Web Services依赖项。 以下清单显示了 `pom.xml` 选择Maven时创建的文件: ~~~ <?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>2.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>consuming-web-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consuming-web-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> ~~~ 以下清单显示了 `build.gradle` 选择Gradle时创建的文件: ~~~ plugins { id 'org.springframework.boot' version '2.3.2.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } } test { useJUnitPlatform() } ~~~ ## 修改构建文件 Spring Initializr创建的构建文件需要大量的工作才能完成本指南。 另外,对 `pom.xml` (对于Maven)和 `build.gradle` (对于Gradle)有很大的不同。 ### Maven For Maven, you need to add a dependency, a profile, and a WSDL generation plugin. 以下清单显示了您需要在Maven中添加的依赖项: ~~~ <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> ~~~ 以下清单显示了要使其与Java 11一起使用时需要在Maven中添加的配置文件: ~~~ <profiles> <profile> <id>java11</id> <activation> <jdk>[11,)</jdk> </activation> <dependencies> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> </dependency> </dependencies> </profile> </profiles> ~~~ “ [基于WSDL生成域对象”](https://spring.io/guides/gs/consuming-web-service/#initial) 部分介绍了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>2.3.2.RELEASE</version> <!-- lookup parent from repository --> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>consuming-web-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consuming-web-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 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> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <!-- tag::profile[] --> <profiles> <profile> <id>java11</id> <activation> <jdk>[11,)</jdk> </activation> <dependencies> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> </dependency> </dependencies> </profile> </profiles> <!-- end::profile[] --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- tag::wsdl[] --> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.14.0</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaLanguage>WSDL</schemaLanguage> <generatePackage>com.example.consumingwebservice.wsdl</generatePackage> <schemas> <schema> <url>http://localhost:8080/ws/countries.wsdl</url> </schema> </schemas> </configuration> </plugin> <!-- end::wsdl[] --> </plugins> </build> </project> ~~~ ### Gradle For Gradle, you need to add a dependency, a configuration, a `bootJar` section, and a WSDL generation plugin. The following listing shows the dependency you need to add in Gradle: ~~~ implementation ('org.springframework.boot:spring-boot-starter-web-services') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' } implementation 'org.springframework.ws:spring-ws-core' // For Java 11: implementation 'org.glassfish.jaxb:jaxb-runtime' compile(files(genJaxb.classesDir).builtBy(genJaxb)) jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7" ~~~ 注意排除了Tomcat。 如果允许Tomcat在此版本中运行,则将与提供国家/地区数据的Tomcat实例发生端口冲突。 以下清单显示了 `bootJar` 您需要在Gradle中添加的部分: ~~~ bootJar { baseName = 'gs-consuming-web-service' version = '0.0.1' } ~~~ “ [基于WSDL生成域对象”](https://spring.io/guides/gs/consuming-web-service/#initial) 部分介绍了WSDL生成插件。 以下清单显示了最终的 `build.gradle` 文件: ~~~ plugins { id 'org.springframework.boot' version '2.3.2.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' // tag::configurations[] configurations { jaxb } // end::configurations[] repositories { mavenCentral() } // tag::wsdl[] task genJaxb { ext.sourcesDir = "${buildDir}/generated-sources/jaxb" ext.classesDir = "${buildDir}/classes/jaxb" ext.schema = "http://localhost:8080/ws/countries.wsdl" outputs.dir classesDir doLast() { project.ant { taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask", classpath: configurations.jaxb.asPath mkdir(dir: sourcesDir) mkdir(dir: classesDir) xjc(destdir: sourcesDir, schema: schema, package: "com.example.consumingwebservice.wsdl") { arg(value: "-wsdl") produces(dir: sourcesDir, includes: "**/*.java") } javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true, debugLevel: "lines,vars,source", classpath: configurations.jaxb.asPath) { src(path: sourcesDir) include(name: "**/*.java") include(name: "*.java") } copy(todir: classesDir) { fileset(dir: sourcesDir, erroronmissingdir: false) { exclude(name: "**/*.java") } } } } } // end::wsdl[] dependencies { // tag::dependency[] implementation ('org.springframework.boot:spring-boot-starter-web-services') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' } implementation 'org.springframework.ws:spring-ws-core' // For Java 11: implementation 'org.glassfish.jaxb:jaxb-runtime' compile(files(genJaxb.classesDir).builtBy(genJaxb)) jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7" // end::dependency[] testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } } test { useJUnitPlatform() } // tag::bootjar[] bootJar { baseName = 'gs-consuming-web-service' version = '0.0.1' } // end::bootjar[] ~~~ ## Generate Domain Objects Based on a WSDL The interface to a SOAP web service is captured in [WSDL](https://en.wikipedia.org/wiki/Web_Services_Description_Language). JAXB provides a way to generate Java classes from WSDL (or rather, the XSD contained in the `<Types/>` section of the WSDL). You can find the WSDL for the country service at `[http://localhost:8080/ws/countries.wsdl](http://localhost:8080/ws/countries.wsdl)`. 要从Maven中的WSDL生成Java类,您需要以下插件设置: ~~~ <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.14.0</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaLanguage>WSDL</schemaLanguage> <generatePackage>com.example.consumingwebservice.wsdl</generatePackage> <schemas> <schema> <url>http://localhost:8080/ws/countries.wsdl</url> </schema> </schemas> </configuration> </plugin> ~~~ 此设置将为在指定URL处找到的WSDL生成类,并将这些类放入 `com.example.consumingwebservice.wsdl`包裹。 要生成该代码,请运行 `./mvnw compile` 然后看 `target/generated-sources` 如果您想检查它是否有效。 要对Gradle进行同样的操作,您将在构建文件中需要以下内容: ~~~ task genJaxb { ext.sourcesDir = "${buildDir}/generated-sources/jaxb" ext.classesDir = "${buildDir}/classes/jaxb" ext.schema = "http://localhost:8080/ws/countries.wsdl" outputs.dir classesDir doLast() { project.ant { taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask", classpath: configurations.jaxb.asPath mkdir(dir: sourcesDir) mkdir(dir: classesDir) xjc(destdir: sourcesDir, schema: schema, package: "com.example.consumingwebservice.wsdl") { arg(value: "-wsdl") produces(dir: sourcesDir, includes: "**/*.java") } javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true, debugLevel: "lines,vars,source", classpath: configurations.jaxb.asPath) { src(path: sourcesDir) include(name: "**/*.java") include(name: "*.java") } copy(todir: classesDir) { fileset(dir: sourcesDir, erroronmissingdir: false) { exclude(name: "**/*.java") } } } } } ~~~ As Gradle does not (yet) have a JAXB plugin, it involves an Ant task, which makes it a bit more complex than in Maven. To generate that code run `./gradlew compileJava` and then look in `build/generated-sources` if you want to check that it worked. 在这两种情况下,JAXB域对象生成过程都已连接到构建工具的生命周期中,因此一旦构建成功,就无需运行任何额外的步骤。 ## 创建国家服务客户 要创建Web服务客户端,您必须扩展 [`WebServiceGatewaySupport`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/client/core/support/WebServiceGatewaySupport.html) 对您的操作进行分类和编码,如下例所示(摘自 `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( "http://spring.io/guides/gs-producing-web-service/GetCountryRequest")); return response; } } ~~~ The client contains one method (`getCountry`) that does the actual SOAP exchange. 在这种方法中, `GetCountryRequest` 和 `GetCountryResponse`类是从WSDL派生的,并且是在JAXB生成过程中 (在 [生成的 基于WSDL的生成域对象中进行了介绍](https://spring.io/guides/gs/consuming-web-service/#initial) )。 它创建了 `GetCountryRequest` 请求对象并使用 `country`参数(国家名称)。 在打印出国家名称后,它使用 [`WebServiceTemplate`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/client/core/WebServiceTemplate.html) 由...提供 `WebServiceGatewaySupport`基类做实际的SOAP交换。 它通过了 `GetCountryRequest` 请求对象(以及 `SoapActionCallback`传递 的 [SOAPAction](https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528) 带有请求 标头),如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; } } ~~~ The `marshaller` is pointed at the collection of generated domain objects and will use them to both serialize and deserialize between XML and POJOs. The `countryClient` is created and configured with the URI of the country service shown earlier. It is also configured to use the JAXB marshaller. ## Run the Application This application is packaged up to run from the console and retrieve the data for a given country name, as the following listing (from `src/main/java/com/example/consumingwebservice/ConsumingWebServiceApplication.java`) shows: ~~~ 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 quoteClient) { return args -> { String country = "Spain"; if (args.length > 0) { country = args[0]; } GetCountryResponse response = quoteClient.getCountry(country); System.err.println(response.getCountry().getCurrency()); }; } } ~~~ 这 `main()` 该方法符合 [`SpringApplication`](https://docs.spring.io/spring-boot/docs/2.3.2.RELEASE/api/org/springframework/boot/SpringApplication.html) 帮手类,提供 `CountryConfiguration.class` 作为其论点 `run()`方法。 这告诉Spring从中读取注释元数据 `CountryConfiguration` 并将其作为Spring应用程序上下文中的组件进行管理。 此应用程序经过硬编码以查找“西班牙”。 在本指南的后面,您将看到如何在不编辑代码的情况下输入其他符号。 ### 建立可执行的JAR 您可以使用Gradle或Maven从命令行运行该应用程序。 您还可以构建一个包含所有必需的依赖项,类和资源的可执行JAR文件,然后运行该文件。 生成可执行jar使得在整个开发生命周期中,跨不同环境等等的情况下,都可以轻松地将服务作为应用程序进行发布,版本控制和部署。 如果您使用Gradle,则可以通过使用以下命令运行该应用程序 `./gradlew bootRun`。 或者,您可以通过使用以下命令构建JAR文件: `./gradlew build` 然后运行JAR文件,如下所示: ~~~ java -jar build/libs/gs-consuming-web-service-0.1.0.jar ~~~ 如果您使用Maven,则可以通过使用以下命令运行该应用程序 `./mvnw spring-boot:run`。 或者,您可以使用以下命令构建JAR文件: `./mvnw clean package` 然后运行JAR文件,如下所示: ~~~ java -jar target/gs-consuming-web-service-0.1.0.jar ~~~ The steps described here create a runnable JAR. You can also build a classic WAR file. Logging output is displayed. The service should be up and running within a few seconds. The following listing shows the initial response: ~~~ 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服务。