本文介绍 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
依赖。
org.springframework.boot
spring-boot-starter-web-services
wsdl4j
wsdl4j
1.6.3
- 在
pom
文件中添加jaxb2-maven-plugin
插件。
org.codehaus.mojo
jaxb2-maven-plugin
2.5.0
xjc
xjc
src/main/java
tutorial.spring.boot.soap.producer.generated
false
jaxb2-maven-plugin
能够实现 Java 类和 XML Schema 间的转换,配置说明:
-
sources
:xsd 文件目录 -
outputDirectory
:生成 Java 类文件的根目录 -
packageName
:生成 Java 类文件的包路径 -
clearOutputDir
:重新生成前是否需要清空目录
完整的 pom
文件如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.2.RELEASE
tutorial.spring.boot
spring-boot-soap-producer
0.0.1-SNAPSHOT
spring-boot-soap-producer
Demo project for Spring Boot SOAP producer
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-web-services
wsdl4j
wsdl4j
1.6.3
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-maven-plugin
org.codehaus.mojo
jaxb2-maven-plugin
2.5.0
xjc
xjc
src/main/java
tutorial.spring.boot.soap.producer.generated
false
- 创建 XML Schema 定义领域模型,在
src/main/resources/xsd
目录下添加user.xsd
文件。
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://
。
- 创建服务端点:定义一个
@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 文件中可以看出:
- 接口名称(
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 描述文件,执行测试。