乐趣区

spring boot 开发soap webservice

介绍
spring boot web 模块提供了 RestController 实现 restful,第一次看到这个名字的时候以为还有 SoapController,很可惜没有,对于 soap webservice 提供了另外一个模块 spring-boot-starter-web-services 支持。本文介绍如何在 spring boot 中开发 soap webservice 接口,以及接口如何同时支持 soap 和 restful 两种协议。
soap webservice
Web service 是一个平台独立的,低耦合的,自包含的、基于可编程的 web 的应用程序,既可以是 soap webservice 也可以是 rest webservice,在 rest 还没出来之前,我们说 webservice 一般是指基于 soap 协议进行通信的 web 应用程序。
在开始之前,我觉得有必要了解下 soap webservice,具体的概念网上可以找到很多资料,但网上资料概念性较强,而且 soap 协议使用的是 xml 进行通信,相信 xml 里面一个 namespace 就能吓跑一大堆人,所以这里不讨论具体的 soap 协议细节,我想通过一个例子来说明什么是 soap webservice,通过该例子,你能了解 soap webservice 其运作原理,当然如果你觉得你对这个已经很了解了,大可跳过本章节,本章节跟后面的内容没有任何关系。
假设我们开发了一个 web 接口,想给别人用,我们要怎么办

部署接口到服务器
编写接口文档,写清楚接口是通过什么方法调的,输入参数是什么,输出参数是什么,错误时返回什么。

那问题来了,我们能不能只把接口部署到服务器上,然后接口不单能提供具体的服务,而且还能自动生成一份标准的接口文档,把接口信息都记录在该文档里,如果能做到,是不是能做到 ” 接口即文档 ” 的目的。
那么一个接口的信息包括哪些呢?

接口地址
接口调用方法
接口输入参数
接口输出参数
接口出错返回信息
….

soap webservice 里 wsdl 文件就是接口描述信息。核心的信息就是以上几个。
第二个问题,由于 Web service 是一个平台独立,也就是说,使用接口的人不知道这个 service 是用什么技术开发的,可能是 php 可能是 java 等,但接口的参数和返回的数据都是一样的,要达到这种目的,就需要两个东西,一个是跟平台无关的数据格式,soap 使用的是 xml,一个是通信协议,也就是 soap 协议。
下面就介绍如何不使用任何框架,仅通过 servlet 实现一个 webservice。该 webservice 功能很简单,就是通过一个人的姓名查询这个人的详细信息。
ps:servlet 是 java web 的基础,理解 servlet 对理解整个 java web 非常重要,没写过 servlet 就开始用各种框架写接口就是在胡闹。
1. wsdl 文件
准备以下 wsdl 文件,不要管这个文件是怎么来的,是怎么生成的,我们这次只讲原理,不谈细节,总之,你根据需求写出了这个 wsdl 文件。

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”no”?><wsdl:definitions xmlns:wsdl=”http://schemas.xmlsoap.org/wsdl/” xmlns:sch=”http://www.definesys.com/xml/employee” xmlns:soap=”http://schemas.xmlsoap.org/wsdl/soap/” xmlns:tns=”http://www.definesys.com/xml/employee” targetNamespace=”http://www.definesys.com/xml/employee”>
<wsdl:types>
<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema” elementFormDefault=”qualified” targetNamespace=”http://www.definesys.com/xml/employee”>

<xs:element name=”EmployeeDetailRequest”>
<xs:complexType>
<xs:sequence>
<xs:element name=”name” type=”xs:string”/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name=”EmployeeDetailResponse”>
<xs:complexType>
<xs:sequence>
<xs:element name=”Employee” type=”tns:Employee”/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name=”Employee”>
<xs:sequence>
<xs:element name=”name” type=”xs:string”/>
<xs:element name=”email” type=”xs:string”/>
</xs:sequence>
</xs:complexType>

</xs:schema>
</wsdl:types>
<wsdl:message name=”EmployeeDetailRequest”>
<wsdl:part element=”tns:EmployeeDetailRequest” name=”EmployeeDetailRequest”>
</wsdl:part>
</wsdl:message>
<wsdl:message name=”EmployeeDetailResponse”>
<wsdl:part element=”tns:EmployeeDetailResponse” name=”EmployeeDetailResponse”>
</wsdl:part>
</wsdl:message>
<wsdl:portType name=”Employee”>
<wsdl:operation name=”EmployeeDetail”>
<wsdl:input message=”tns:EmployeeDetailRequest” name=”EmployeeDetailRequest”>
</wsdl:input>
<wsdl:output message=”tns:EmployeeDetailResponse” name=”EmployeeDetailResponse”>
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name=”EmployeeSoap11″ type=”tns:Employee”>
<soap:binding style=”document” transport=”http://schemas.xmlsoap.org/soap/http”/>
<wsdl:operation name=”EmployeeDetail”>
<soap:operation soapAction=””/>
<wsdl:input name=”EmployeeDetailRequest”>
<soap:body use=”literal”/>
</wsdl:input>
<wsdl:output name=”EmployeeDetailResponse”>
<soap:body use=”literal”/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name=”EmployeeService”>
<wsdl:port binding=”tns:EmployeeSoap11″ name=”EmployeeSoap11″>
<soap:address location=”http://localhost:8081/ws-servlet/ws/employee-detail”/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
soap:address location 里面端口号需要修改为 servlet 运行的端口号。
从以下 xml 片段可以看出

<wsdl:binding name=”EmployeeSoap11″ type=”tns:Employee”>
<soap:binding style=”document” transport=”http://schemas.xmlsoap.org/soap/http”/>
<wsdl:operation name=”EmployeeDetail”>
<soap:operation soapAction=””/>
<wsdl:input name=”EmployeeDetailRequest”>
<soap:body use=”literal”/>
</wsdl:input>
<wsdl:output name=”EmployeeDetailResponse”>
<soap:body use=”literal”/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name=”EmployeeService”>
<wsdl:port binding=”tns:EmployeeSoap11″ name=”EmployeeSoap11″>
<soap:address location=”http://localhost:8081/ws-servlet/ws/employee-detail”/>
</wsdl:port>
</wsdl:service>

接口名称是 EmployeeDetail(wsdl:operation)
接口输入参数是 EmployeeDetailRequest(wsdl:input)
接口输出参数是 EmployeeDetailResponse(wsdl:output)
接口地址是 http://localhost:8081/ws-servlet/ws/employee-detail(soap:address)

2. 获取 wsdl 文件 servlet
package com.definesys.demo.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 1:45
* @history: 1.2019/1/5 created by jianfeng.zheng
*/
public class WsdlServlet extends HttpServlet {
public static final String WSDL_XML = “<?xml version=\”1.0\” encoding=\”UTF-8\” standalone=\”no\”?><wsdl:definitions xmlns:wsdl=\”http://schemas.xmlsoap.org/wsdl/\” xmlns:sch=\”http://www.definesys.com/xml/employee\” xmlns:soap=\”http://schemas.xmlsoap.org/wsdl/soap/\” xmlns:tns=\”http://www.definesys.com/xml/employee\” targetNamespace=\”http://www.definesys.com/xml/employee\”>\n” +
” <wsdl:types>\n” +
” <xs:schema xmlns:xs=\”http://www.w3.org/2001/XMLSchema\” elementFormDefault=\”qualified\” targetNamespace=\”http://www.definesys.com/xml/employee\”>\n” +
“\n” +
” <xs:element name=\”EmployeeDetailRequest\”>\n” +
” <xs:complexType>\n” +
” <xs:sequence>\n” +
” <xs:element name=\”name\” type=\”xs:string\”/>\n” +
” </xs:sequence>\n” +
” </xs:complexType>\n” +
” </xs:element>\n” +
“\n” +
” <xs:element name=\”EmployeeDetailResponse\”>\n” +
” <xs:complexType>\n” +
” <xs:sequence>\n” +
” <xs:element name=\”Employee\” type=\”tns:Employee\”/>\n” +
” </xs:sequence>\n” +
” </xs:complexType>\n” +
” </xs:element>\n” +
“\n” +
” <xs:complexType name=\”Employee\”>\n” +
” <xs:sequence>\n” +
” <xs:element name=\”name\” type=\”xs:string\”/>\n” +
” <xs:element name=\”email\” type=\”xs:string\”/>\n” +
” </xs:sequence>\n” +
” </xs:complexType>\n” +
“\n” +
“</xs:schema>\n” +
” </wsdl:types>\n” +
” <wsdl:message name=\”EmployeeDetailRequest\”>\n” +
” <wsdl:part element=\”tns:EmployeeDetailRequest\” name=\”EmployeeDetailRequest\”>\n” +
” </wsdl:part>\n” +
” </wsdl:message>\n” +
” <wsdl:message name=\”EmployeeDetailResponse\”>\n” +
” <wsdl:part element=\”tns:EmployeeDetailResponse\” name=\”EmployeeDetailResponse\”>\n” +
” </wsdl:part>\n” +
” </wsdl:message>\n” +
” <wsdl:portType name=\”Employee\”>\n” +
” <wsdl:operation name=\”EmployeeDetail\”>\n” +
” <wsdl:input message=\”tns:EmployeeDetailRequest\” name=\”EmployeeDetailRequest\”>\n” +
” </wsdl:input>\n” +
” <wsdl:output message=\”tns:EmployeeDetailResponse\” name=\”EmployeeDetailResponse\”>\n” +
” </wsdl:output>\n” +
” </wsdl:operation>\n” +
” </wsdl:portType>\n” +
” <wsdl:binding name=\”EmployeeSoap11\” type=\”tns:Employee\”>\n” +
” <soap:binding style=\”document\” transport=\”http://schemas.xmlsoap.org/soap/http\”/>\n” +
” <wsdl:operation name=\”EmployeeDetail\”>\n” +
” <soap:operation soapAction=\”\”/>\n” +
” <wsdl:input name=\”EmployeeDetailRequest\”>\n” +
” <soap:body use=\”literal\”/>\n” +
” </wsdl:input>\n” +
” <wsdl:output name=\”EmployeeDetailResponse\”>\n” +
” <soap:body use=\”literal\”/>\n” +
” </wsdl:output>\n” +
” </wsdl:operation>\n” +
” </wsdl:binding>\n” +
” <wsdl:service name=\”EmployeeService\”>\n” +
” <wsdl:port binding=\”tns:EmployeeSoap11\” name=\”EmployeeSoap11\”>\n” +
” <soap:address location=\”http://localhost:8081/ws-servlet/ws/employee-detail\”/>\n” +
” </wsdl:port>\n” +
” </wsdl:service>\n” +
“</wsdl:definitions>”;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType(“text/xml”);
resp.getOutputStream().write(WSDL_XML.getBytes());
}
}
是不是很简单,是的,为了简单,我直接将 wsdl 文件用变量存储,我们还需要配置下 web.xml
web.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<web-app xmlns=”http://xmlns.jcp.org/xml/ns/javaee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd”
version=”3.1″>
<servlet>
<servlet-name>wsdl</servlet-name>
<servlet-class>com.definesys.demo.servlet.WsdlServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>wsdl</servlet-name>
<url-pattern>/ws/employee</url-pattern>
</servlet-mapping>
</web-app>
这样我们访问 http://localhost:8080/ws/employee 就能返回一个 wsdl 文件,也就是接口描述文件。在 wsdl 文件里,我们定义接口地址为 http://localhost:8080/ws/employee-detail,接下来我们就要实现这个接口。
3. 业务 servlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 2:56
* @history: 1.2019/1/5 created by jianfeng.zheng
*/
public class EmployeeServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String response = “<SOAP-ENV:Envelope xmlns:SOAP-ENV=\”http://schemas.xmlsoap.org/soap/envelope/\”>\n” +
” <SOAP-ENV:Header/>\n” +
” <SOAP-ENV:Body>\n” +
” <ns2:EmployeeDetailResponse xmlns:ns2=\”http://www.definesys.com/xml/employee\”>\n” +
” <ns2:Employee>\n” +
” <ns2:name>jianfeng</ns2:name>\n” +
” <ns2:email>jianfeng.zheng@definesys.com</ns2:email>\n” +
” </ns2:Employee>\n” +
” </ns2:EmployeeDetailResponse>\n” +
” </SOAP-ENV:Body>\n” +
“</SOAP-ENV:Envelope>”;
resp.getOutputStream().write(response.getBytes());
}
}
这里不做任何业务处理,不做 xml 转 bean,不做 bean 转 xml,就是这么暴力,直接返回 xml,但他仍是一个 soap 服务,支持所有 soap 工具调用。
将 servlet 配置到 web.xml 里
web.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<web-app xmlns=”http://xmlns.jcp.org/xml/ns/javaee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd”
version=”3.1″>
<servlet>
<servlet-name>wsdl</servlet-name>
<servlet-class>com.definesys.demo.servlet.WsdlServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>employee</servlet-name>
<servlet-class>com.definesys.demo.servlet.EmployeeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>wsdl</servlet-name>
<url-pattern>/ws/employee</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>employee</servlet-name>
<url-pattern>/ws/employee-detail</url-pattern>
</servlet-mapping>
</web-app>
/ws/employee-detail 这个地址必须和 wsdl 文件里定义的保持一致,不然服务无法被找到。
4. 测试
使用 soapui 测试我们的 webservice,通过地址 http://localhost:8081/ws-servlet/ws/employee 导入 wsdl 文件,测试接口,返回我们在业务 servlet 里面写死的内容。恭喜你,你已经不依赖任何第三方包完成了一个 soap webservice。
当然这个只是一个玩具,但框架就是在上面的基础上进行扩展,增加 wsdl 文件自动生成,xml 转 java,java 转 xml,xml 校验,错误处理等功能,如果你有时间,你也可以写一个 soap webservice 框架。
代码已经上传至 github,欢迎 star,开始进入正题,偏的有点远。
spring boot 开发 soap webservice
1. 创建 spring boot 工程
你可以通过 spring initializr 初始化 spring boot 工程,也可以通过 inte idea 的 spring initializr 插件进行初始化,个人推荐后面这种。
2. 添加依赖
添加 soap webservice 相关依赖包和插件,
pom.xml
<!– 依赖 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>

<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>

<!– 插件 –>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
<!–<schemaFiles>employee.xsd</schemaFiles>–>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<packageName>com.definesys.tutorial.ws.type</packageName>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
插件 jaxb2 能够实现 java 和 xml 之间互转,下面是几个参数的说明

schemaDirectory:xsd 文件目录
schemaFiles:指定 schemaDirectory 下的 xsd 文件,多个用逗号隔开,必须指定 schemaDirectory
outputDirectory:生成 java 文件保存目录
packageName:生成 java 文件包路径
clearOutputDir:重新生成前是否需要清空目录

3. 编写 xsd 文件
假设我们的需求是通过员工工号查询员工详细信息,根据需求编写以下 xsd 文件,并保存在 /src/main/resources/ 目录下。
employee.xsd
<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema” xmlns:tns=”http://www.definesys.com/xml/employee”
targetNamespace=”http://www.definesys.com/xml/employee” elementFormDefault=”qualified”>

<xs:element name=”EmployeeDetailRequest”>
<xs:complexType>
<xs:sequence>
<xs:element name=”code” type=”xs:string”/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name=”EmployeeDetailResponse”>
<xs:complexType>
<xs:sequence>
<xs:element name=”Employee” type=”tns:Employee”/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name=”Employee”>
<xs:sequence>
<xs:element name=”code” type=”xs:string”/>
<xs:element name=”name” type=”xs:string”/>
<xs:element name=”email” type=”xs:string”/>
</xs:sequence>
</xs:complexType>

</xs:schema>
4. 生成 java 类型文件
我们需要根据 xsd 文件生成 java 类型文件,这就要借助 maven 插件 jaxb2,打开终端运行命令 mvn jaxb2:xjc,如果运行正常,就会在目录 com.definesys.tutorial.ws.type 下生成一堆 java 文件,此时文件结构如下:
.
├── java
│   └── com
│   └── definesys
│   └── tutorial
│   └── ws
│   ├── SpringbootWsApplication.java
│   └── type
│   ├── Employee.java
│   ├── EmployeeDetailRequest.java
│   ├── EmployeeDetailResponse.java
│   ├── ObjectFactory.java
│   └── package-info.java
└── resources
├── application.properties
├── employee.xsd
├── static
└── templates
5. 创建配置文件
WebserviceConfig.java
package com.definesys.tutorial.ws;

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.ws.wsdl.wsdl11.Wsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 4:46
* @history: 1.2019/1/5 created by jianfeng.zheng
*/

@EnableWs
@Configuration
public class WebserviceConfig extends WsConfigurerAdapter {

@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, “/ws/*”);
}

@Bean(name = “employee”)
public Wsdl11Definition defaultWsdl11Definition(XsdSchema schema) {
DefaultWsdl11Definition wsdl = new DefaultWsdl11Definition();
wsdl.setPortTypeName(“EmployeePort”);
wsdl.setLocationUri(“/ws/employee-detail”);
wsdl.setTargetNamespace(“http://www.definesys.com/xml/employee”);
wsdl.setSchema(schema);
return wsdl;
}

@Bean
public XsdSchema employeeSchema() {
return new SimpleXsdSchema(new ClassPathResource(“employee.xsd”));
}
}
6. 创建业务服务
EmployeeSoapController.java
package com.definesys.tutorial.ws;

import com.definesys.tutorial.ws.type.Employee;
import com.definesys.tutorial.ws.type.EmployeeDetailRequest;
import com.definesys.tutorial.ws.type.EmployeeDetailResponse;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 4:49
* @history: 1.2019/1/5 created by jianfeng.zheng
*/
@Endpoint
public class EmployeeSoapController {

private static final String NAMESPACE_URI = “http://www.definesys.com/xml/employee”;

@PayloadRoot(namespace = NAMESPACE_URI, localPart = “EmployeeDetailRequest”)
@ResponsePayload
public EmployeeDetailResponse getEmployee(@RequestPayload EmployeeDetailRequest request) {

EmployeeDetailResponse response = new EmployeeDetailResponse();

// 这里只作为演示, 真正开发中需要编写业务逻辑代码
Employee employee = new Employee();
employee.setName(“jianfeng”);
employee.setEmail(“jianfeng.zheng@definesys.com”);
employee.setCode(request.getCode());
response.setEmployee(employee);

return response;
}
}
与 RestController 不一样的是,spring boot soap 是根据请求报文来指定调用的函数,RestController 是根据请求路径来确定。@PayloadRoot 就是关键,如本次请求报文如下:
<soapenv:Envelope xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/” xmlns:emp=”http://www.definesys.com/xml/employee”>
<soapenv:Header/>
<soapenv:Body>
<emp:EmployeeDetailRequest>
<emp:code>?</emp:code>
</emp:EmployeeDetailRequest>
</soapenv:Body>
</soapenv:Envelope>
xmlns:emp=”http://www.definesys.com/xml/employee” 就是 @PayloadRoot.namespace,emp:EmployeeDetailRequest 对应 @PayloadRoot.localPart。理解了这个其他都很好理解。
7. 测试
使用 soapui 进行测试,通过地址 http://localhost:8080/ws/employee.wsdl 导入 wsdl 文件进行测试。
输入报文
<soapenv:Envelope xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/” xmlns:emp=”http://www.definesys.com/xml/employee”>
<soapenv:Header/>
<soapenv:Body>
<emp:EmployeeDetailRequest>
<emp:code>004</emp:code>
</emp:EmployeeDetailRequest>
</soapenv:Body>
</soapenv:Envelope>
输出报文
<SOAP-ENV:Envelope xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”>
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:EmployeeDetailResponse xmlns:ns2=”http://www.definesys.com/xml/employee”>
<ns2:Employee>
<ns2:code>004</ns2:code>
<ns2:name>jianfeng</ns2:name>
<ns2:email>jianfeng.zheng@definesys.com</ns2:email>
</ns2:Employee>
</ns2:EmployeeDetailResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
同时提供 soap 和 restful 两种服务
soap 一般在企业内部用的比较多,做系统间的集成,restful 一般用于移动应用和 h5 应用,如果在企业应用开发里能够同时提供两种协议的支持,将极大提高接口的复用。其实也没有想象中的那么复杂,在本例中,只需把业务逻辑部分用 service 实现再创建一个 RestController 即可,通过设计模式即可解决,不需要引入新的技术。
EmployeeService.java
package com.definesys.tutorial.ws;

import com.definesys.tutorial.ws.type.Employee;
import com.definesys.tutorial.ws.type.EmployeeDetailRequest;
import com.definesys.tutorial.ws.type.EmployeeDetailResponse;
import org.springframework.stereotype.Service;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 5:42
* @history: 1.2019/1/5 created by jianfeng.zheng
*/
@Service
public class EmployeeService {

public EmployeeDetailResponse getEmployee(EmployeeDetailRequest request) {

EmployeeDetailResponse response = new EmployeeDetailResponse();

// 这里只作为演示, 真正开发中需要编写业务逻辑代码
Employee employee = new Employee();
employee.setName(“jianfeng”);
employee.setEmail(“jianfeng.zheng@definesys.com”);
employee.setCode(request.getCode());
response.setEmployee(employee);

return response;
}
}
EmployeeSoapController.java
package com.definesys.tutorial.ws;

import com.definesys.tutorial.ws.type.Employee;
import com.definesys.tutorial.ws.type.EmployeeDetailRequest;
import com.definesys.tutorial.ws.type.EmployeeDetailResponse;
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;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 4:49
* @history: 1.2019/1/5 created by jianfeng.zheng
*/
@Endpoint
public class EmployeeSoapController {

@Autowired
private EmployeeService service;

private static final String NAMESPACE_URI = “http://www.definesys.com/xml/employee”;

@PayloadRoot(namespace = NAMESPACE_URI, localPart = “EmployeeDetailRequest”)
@ResponsePayload
public EmployeeDetailResponse getEmployee(@RequestPayload EmployeeDetailRequest request) {

return service.getEmployee(request);
}
}
EmployeeRestController.java
package com.definesys.tutorial.ws;

import com.definesys.tutorial.ws.type.EmployeeDetailRequest;
import com.definesys.tutorial.ws.type.EmployeeDetailResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
* @Copyright: Shanghai Definesys Company.All rights reserved.
* @Description:
* @author: jianfeng.zheng
* @since: 2019/1/5 下午 5:43
* @history: 1.2019/1/5 created by jianfeng.zheng
*/
@RestController
@RequestMapping(value = “/rest”)
public class EmployeeRestController {

@Autowired
private EmployeeService service;

@RequestMapping(value = “/employee-detail”, method = RequestMethod.POST)
public EmployeeDetailResponse getEmployeeDetail(@RequestBody EmployeeDetailRequest request) {
return service.getEmployee(request);
}
}

测试
$ curl http://localhost:8080/rest/employee-detail -X POST -d ‘{“code”:”004″}’ -H “Content-Type: application/json”
{
“employee”: {
“code”: “004”,
“name”: “jianfeng”,
“email”: “jianfeng.zheng@definesys.com”
}
}
这样就实现了 soap 和 rest 同时提供的目的。
本文代码已提交至 gitlab 欢迎 star
相关参考文档

https://spring.io/guides/gs/producing-web-service/
https://github.com/wls1036/tutorial-springboot-soap
https://github.com/wls1036/pure-ws-servlet

退出移动版