💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 产生SOAP Web服务 本指南将引导您完成使用Spring创建基于SOAP的Web服务服务器的过程。 ## 你会建立什么 您将使用基于WSDL的SOAP Web服务来构建一个服务器,以暴露来自欧洲各个国家的数据。 为了简化示例,您将使用英国,西班牙和波兰的硬编码数据。 ## 你需要什么 * 约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/producing-web-service/#scratch) 。 要 **跳过基础知识** ,请执行以下操作: * [下载](https://github.com/spring-guides/gs-soap-service/archive/master.zip) 并解压缩本指南的源存储库,或使用 对其进行克隆 [Git](https://spring.io/understanding/Git) : `git clone [https://github.com/spring-guides/gs-soap-service.git](https://github.com/spring-guides/gs-soap-service.git)` * 光盘进入 `gs-soap-service/initial` * 继续 [添加Spring-WS依赖项](https://spring.io/guides/gs/producing-web-service/#initial) 。 **完成后** ,您可以根据中的代码检查结果 `gs-soap-service/complete`. ## 从Spring Initializr开始 如果您使用Maven,请访问 [Spring Initializr](https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.4.3.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=producing-web-service&name=producing-web-service&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.producing-web-service&dependencies=web,web-services) 以生成具有所需依赖项的新项目(Spring Web和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.4.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>producing-web-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>producing-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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </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> </plugins> </build> </project> ~~~ 如果使用Gradle,请访问 [Spring Initializr](https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.4.3.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=producing-web-service&name=producing-web-service&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.producing-web-service&dependencies=web,web-services) 以生成具有所需依赖项的新项目(Spring Web和Spring Web Services)。 以下清单显示了 `build.gradle` 选择Gradle时创建的文件: ~~~ plugins { id 'org.springframework.boot' version '2.4.3' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web-services' testImplementation('org.springframework.boot:spring-boot-starter-test') } test { useJUnitPlatform() } ~~~ ### 手动初始化(可选) 如果要手动初始化项目而不是使用前面显示的链接,请按照以下步骤操作: 1. 导航到 [https://start.spring.io](https://start.spring.io) 。 该服务提取应用程序所需的所有依赖关系,并为您完成大部分设置。 2. 选择Gradle或Maven以及您要使用的语言。 本指南假定您选择了Java。 3. 单击 **Dependencies,** 然后选择 **Spring Web** 和 **Spring Web Services** 。 4. 点击 **生成** 。 5. 下载生成的ZIP文件,该文件是使用您的选择配置的Web应用程序的存档。 如果您的IDE集成了Spring Initializr,则可以从IDE中完成此过程。 这俩 pom.xml 和 build.gradle 文件显示其他构建信息,您将在下一步中添加这些信息。 ## 添加Spring-WS依赖项 该项目需要包括 `spring-ws-core` 和 `wsdl4j` 作为构建文件中的依赖项。 以下示例显示了您需要对 `pom.xml` 如果使用Maven,则文件: ~~~ <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> </dependency> ~~~ 以下示例显示了您需要对 `build.gradle` 文件,如果您使用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="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注释。 这些标签可以更轻松地将其提取到本指南中,以进行更详细的说明。 您不需要在自己的构建文件中使用这些注释。 下一步是添加 `genJaxb`任务,Gradle用来生成Java类。 我们需要配置gradle以在以下位置找到这些生成的Java类 `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`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/server/endpoint/annotation/Endpoint.html)注解将类注册为Spring WS,作为处理传入SOAP消息的潜在候选者。 这 [`@PayloadRoot`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/server/endpoint/annotation/PayloadRoot.html) 然后,Spring WS使用注释基于消息的 `namespace` 和 `localPart`. 这 [`@RequestPayload`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/server/endpoint/annotation/RequestPayload.html) 批注指示传入的消息将被映射到方法的 `request` 范围。 这 [`@ResponsePayload`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/server/endpoint/annotation/ResponsePayload.html) 注释使Spring WS将返回的值映射到响应有效负载。 在所有这些代码块中, io.spring.guides 除非您已运行任务以基于WSDL生成域类,否则这些类将在您的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("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`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/transport/http/MessageDispatcherServlet.html)。 注入和设置很重要 [`ApplicationContext`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/ApplicationContext.html) 到 [`MessageDispatcherServlet`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/transport/http/MessageDispatcherServlet.html)。 否则,Spring WS将不会自动检测Spring Bean。 * 命名这个豆 `messageDispatcherServlet`不替代Spring Boot的 [默认值 `DispatcherServlet`豆](https://docs.spring.io/spring-boot/docs/2.4.3/reference/htmlsingle/#howto-switch-off-the-spring-mvc-dispatcherservlet) 。 * [`DefaultMethodEndpointAdapter`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.html)配置注释驱动的Spring WS编程模型。 这样就可以使用各种注释,例如 [`@Endpoint`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/server/endpoint/annotation/Endpoint.html) (较早前提过)。 * [`DefaultWsdl11Definition`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/ws/wsdl/wsdl11/DefaultWsdl11Definition.html) 通过使用公开标准的WSDL 1.1 [`XsdSchema`](https://docs.spring.io/spring-ws/sites/2.0/apidocs/org/springframework/xml/xsd/XsdSchema.html) 您需要为指定bean名称 MessageDispatcherServlet 和 DefaultWsdl11Definition。 Bean名称确定URL,Web服务和生成的WSDL文件可在该URL下使用。 在这种情况下,WSDL将在 :/ws/countries.wsdl" class="bare" one-link-mark="yes">http://&lt;host&gt;:&lt;port&gt;/ws/countries.wsdl. 此配置还使用WSDL位置servlet转换: `servlet.setTransformWsdlLocations(true)`。 如果您访问 [http:// localhost:8080 / ws / countries.wsdl](http://localhost: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,因此您无需处理任何管道或基础结构。 ### Build an executable JAR 您可以使用Gradle或Maven从命令行运行该应用程序。 您还可以构建一个包含所有必需的依赖项,类和资源的可执行JAR文件,然后运行该文件。 生成可执行jar使得在整个开发生命周期中,跨不同环境等等的情况下,都可以轻松地将服务作为应用程序进行发布,版本控制和部署。 如果您使用Gradle,则可以通过使用以下命令运行该应用程序 `./gradlew bootRun`。 或者,您可以通过使用以下命令构建JAR文件: `./gradlew build` 然后运行JAR文件,如下所示: ~~~ java -jar build/libs/gs-soap-service-0.1.0.jar ~~~ 如果您使用Maven,则可以通过使用以下命令运行该应用程序 `./mvnw spring-boot:run`。 或者,您可以使用以下命令构建JAR文件: `./mvnw clean package` 然后运行JAR文件,如下所示: ~~~ java -jar target/gs-soap-service-0.1.0.jar ~~~ 此处描述的步骤将创建可运行的JAR。 您还可以 构建经典的WAR文件 。 显示日志记录输出。 该服务应在几秒钟内启动并运行。 ## 测试应用 现在该应用程序正在运行,您可以对其进行测试。 创建一个名为 `request.xml` 包含以下SOAP请求: ~~~ <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接口时,有一些选择。 您可以使用类似于 [SoapUI的](http://www.soapui.org) 工具,也可以在\* 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 &gt; output.xml and xmllint --format output.xml 看到格式很好的结果。 ## 概括 恭喜你! 您已经使用Spring Web Services开发了基于SOAP的服务。