Spring Boot 开发 SOAP 服务

作者: 又语 | 来源:发表于2020-01-06 17:19 被阅读0次

    本文介绍 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)

    基础示例

    1. 创建 Spring Boot 工程,参考:IntelliJ IDEA 创建 Spring Boot 工程

    2. pom 文件中添加 spring-boot-starter-web-serviceswsdl4j 依赖。

    <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>
    
    1. 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>
    
    1. 创建 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。

    1. 执行 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
    
    1. 创建 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。

    注意:必须为 MessageDispatcherServletDefaultWsdl11Definition 实例指定名称,通过指定名称确定 WSDL URL。本文示例中 MessageDispatcherServlet 实例名称为 wsDefaultWsdl11Definition 实例名称为 user,因此 WSDL URL 是 http://<host>:<port>/ws/user.wsdl

    1. 创建服务端点:定义一个 @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 通过此注解查找匹配消息 namespacelocalPart 的处理方法。
    • @RequestPayload
      标识传入的消息将被映射到方法的哪个入参。
    • @ResponsePayload
      此注解标识 Spring WS 将方法返回值映射到响应负载。
    1. 启动应用后使用浏览器访问 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
    1. 使用 Soap UI 工具测试。

    总结

    Spring Boot 开发 SOAP 服务的步骤:

    1. 创建 Spring Boot 应用,添加 spring-boot-starter-web-serviceswsdl4j 依赖及 jaxb2-maven-plugin 插件;
    2. 创建 xsd 文件;
    3. 执行 mvn compile 生成由 xsd 文件生产 Java 类;
    4. 创建 Web Service 配置类;
    5. 创建业务服务类;
    6. 启动应用,通过浏览器查看 wsdl 描述文件,执行测试。

    相关文章

      网友评论

        本文标题:Spring Boot 开发 SOAP 服务

        本文链接:https://www.haomeiwen.com/subject/xpbkactx.html