本文介绍 Spring Boot 2 开发 SOAP 服务的方法。
目录
- SOAP 简介
- 开发环境
- 基础示例
- 总结
SOAP 简介
SOAP,Simple Object Access Protocol,简单对象访问协议,是一种基于 XML 实现网络中数据交换的通信协议。
一条 SOAP 消息就是一个普通的 XML 文档,包含以下元素:
- 必需的
Envelope
元素,可把此 XML 文档标识为一条 SOAP 消息 - 可选的
Header
元素,包含头部信息 - 必需的
Body
元素,包含所有的调用和响应信息 - 可选的
Fault
元素,提供有关在处理此消息所发生错误的信息
语法规则:
- SOAP 消息必须用 XML 来编码
- SOAP 消息必须使用 SOAP Envelope 命名空间
- SOAP 消息必须使用 SOAP Encoding 命名空间
- SOAP 消息不能包含 DTD 引用
- SOAP 消息不能包含 XML 处理指令
SOAP 是 Web Service 三要素之一,是用来描述传递信息的格式,另外两个元素:
- WSDL,Web Services Description Language,描述如何访问具体接口。
- UDDI,Universal Description Discovery and Integration,管理、分发和查询 Web Service。
开发环境
- Oracle JDK 1.8.0_201
- Apache Maven 3.6.0
- IntelliJ IDEA (Version 2018.3.3)
基础示例
-
创建 Spring Boot 工程,参考:IntelliJ IDEA 创建 Spring Boot 工程。
-
在
pom
文件中添加spring-boot-starter-web-services
和wsdl4j
依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</dependency>
- 在
pom
文件中添加jaxb2-maven-plugin
插件。
<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>src/main/resources/xsd</source>
</sources>
<outputDirectory>src/main/java</outputDirectory>
<packageName>tutorial.spring.boot.soap.producer.generated</packageName>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
jaxb2-maven-plugin
能够实现 Java 类和 XML Schema 间的转换,配置说明:
-
sources
:xsd 文件目录 -
outputDirectory
:生成 Java 类文件的根目录 -
packageName
:生成 Java 类文件的包路径 -
clearOutputDir
:重新生成前是否需要清空目录
完整的 pom
文件如下:
<?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.2.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>tutorial.spring.boot</groupId>
<artifactId>spring-boot-soap-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-soap-producer</name>
<description>Demo project for Spring Boot SOAP producer</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-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</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>
<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>src/main/resources/xsd</source>
</sources>
<outputDirectory>src/main/java</outputDirectory>
<packageName>tutorial.spring.boot.soap.producer.generated</packageName>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 创建 XML Schema 定义领域模型,在
src/main/resources/xsd
目录下添加user.xsd
文件。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://tutorial.spring.boot/soap/produce/user"
targetNamespace="http://tutorial.spring.boot/soap/produce/user"
elementFormDefault="qualified">
<xs:complexType name="User">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="birth" type="xs:date"/>
<xs:element name="gender" type="tns:Gender"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Gender">
<xs:restriction base="xs:string">
<xs:enumeration value="Male"/>
<xs:enumeration value="Female"/>
<xs:enumeration value="Unknown"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="UserRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="UserResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="tns:User"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Web Service 领域模型定义在 XML Schema(XSD) 文件中,Spring-WS 会自动导出 WSDL。
- 执行
mvn compile
生成 Java 类文件,生成文件:
|-- src
|-- main
|-- java
|-- META-INF.JAXB
|-- episode_xjc.xjb
|-- tutorial.spring.boot.soap.producer
|-- generated
|-- Gender.java
|-- ObjectFactory.java
|-- package-info.java
|-- User.java
|-- UserRequest.java
|-- UserResponse.java
- 创建 Web Service 配置类。
package tutorial.spring.boot.soap.producer.config;
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.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.ws.wsdl.wsdl11.Wsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@EnableWs
@Configuration
public class WebServiceConfig {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "user")
public Wsdl11Definition defaultWsdl11Definition(XsdSchema schema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("UserPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://tutorial.spring.boot/soap/produce/user");
wsdl11Definition.setSchema(schema);
return wsdl11Definition;
}
@Bean
public XsdSchema userSchema() {
return new SimpleXsdSchema(new ClassPathResource("xsd/user.xsd"));
}
}
说明:
- Spring WS 使用
MessageDispatcherServlet
处理 SOAP 消息,所以创建 Web Service 配置需要新建一个MessageDispatcherServlet
实例,并将应用上下文ApplicationContext
注入该实例。 -
MessageDispatcherServlet
实例命名为messageDispatcherServlet
并不会替换 Spring Boot 默认的DispatcherServlet
bean。 -
DefaultWsdl11Definition
使用 XSD Schema 暴露标准的 WSDL 1.1。
注意:必须为 MessageDispatcherServlet
和 DefaultWsdl11Definition
实例指定名称,通过指定名称确定 WSDL URL。本文示例中 MessageDispatcherServlet
实例名称为 ws
,DefaultWsdl11Definition
实例名称为 user
,因此 WSDL URL 是 http://<host>:<port>/ws/user.wsdl
。
- 创建服务端点:定义一个
@Endpoint
注解的 POJO 类处理传入的 SOAP 请求。
package tutorial.spring.boot.soap.producer.controller;
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 tutorial.spring.boot.soap.producer.generated.Gender;
import tutorial.spring.boot.soap.producer.generated.User;
import tutorial.spring.boot.soap.producer.generated.UserRequest;
import tutorial.spring.boot.soap.producer.generated.UserResponse;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import java.time.LocalDate;
@Endpoint
public class UserController {
private static final String NAMESPACE_URI = "http://tutorial.spring.boot/soap/produce/user";
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "UserRequest")
@ResponsePayload
public UserResponse getUser(@RequestPayload UserRequest request) throws DatatypeConfigurationException {
UserResponse response = new UserResponse();
User user = new User();
String name = request.getName();
user.setName(name);
switch (name) {
case "Mike":
user.setBirth(
DatatypeFactory.newInstance().newXMLGregorianCalendar(
LocalDate.of(2000, 1, 1).toString()
)
);
user.setGender(Gender.MALE);
break;
case "Ketty":
user.setBirth(
DatatypeFactory.newInstance().newXMLGregorianCalendar(
LocalDate.of(2010, 12, 31).toString()
)
);
user.setGender(Gender.FEMALE);
break;
default:
user.setGender(Gender.UNKNOWN);
break;
}
response.setUser(user);
return response;
}
}
说明:
-
@Endpoint
注解类将被注册到 Spring WS 中用于处理传入的 SOAP 消息。 -
@PayloadRoot
Spring WS 通过此注解查找匹配消息namespace
和localPart
的处理方法。 -
@RequestPayload
标识传入的消息将被映射到方法的哪个入参。 -
@ResponsePayload
此注解标识 Spring WS 将方法返回值映射到响应负载。
- 启动应用后使用浏览器访问
http://127.0.0.1:8080/ws/user.wsdl
。
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://tutorial.spring.boot/soap/produce/user" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://tutorial.spring.boot/soap/produce/user" targetNamespace="http://tutorial.spring.boot/soap/produce/user">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://tutorial.spring.boot/soap/produce/user">
<xs:complexType name="User">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="birth" type="xs:date"/>
<xs:element name="gender" type="tns:Gender"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Gender">
<xs:restriction base="xs:string">
<xs:enumeration value="Male"/>
<xs:enumeration value="Female"/>
<xs:enumeration value="Unknown"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="UserRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="UserResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="tns:User"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="UserRequest">
<wsdl:part element="tns:UserRequest" name="UserRequest"> </wsdl:part>
</wsdl:message>
<wsdl:message name="UserResponse">
<wsdl:part element="tns:UserResponse" name="UserResponse"> </wsdl:part>
</wsdl:message>
<wsdl:portType name="UserPort">
<wsdl:operation name="User">
<wsdl:input message="tns:UserRequest" name="UserRequest"> </wsdl:input>
<wsdl:output message="tns:UserResponse" name="UserResponse"> </wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="UserPortSoap11" type="tns:UserPort">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="User">
<soap:operation soapAction=""/>
<wsdl:input name="UserRequest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="UserResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="UserPortService">
<wsdl:port binding="tns:UserPortSoap11" name="UserPortSoap11">
<soap:address location="http://127.0.0.1:8080/ws"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
从 WSDL 文件中可以看出:
- 接口名称(
wsdl:operation
):User
- 输入参数(
wsdl:input
):UserRequest
- 输出参数(
wsdl:output
):UserResponse
- 接口地址(
soap:address
):http://127.0.0.1:8080/ws
- 使用 Soap UI 工具测试。
总结
Spring Boot 开发 SOAP 服务的步骤:
- 创建 Spring Boot 应用,添加
spring-boot-starter-web-services
和wsdl4j
依赖及jaxb2-maven-plugin
插件; - 创建 xsd 文件;
- 执行
mvn compile
生成由 xsd 文件生产 Java 类; - 创建 Web Service 配置类;
- 创建业务服务类;
- 启动应用,通过浏览器查看 wsdl 描述文件,执行测试。
网友评论