手记

如何在 Spring Boot 中将 SOAP Web Service 转换为 RESTful API

在现代微服务架构中,RESTful API 是主流,但很多遗留系统(Legacy Systems)或第三方服务仍然使用 SOAP 协议。当我们想要重构系统或为前端提供标准 REST 接口时,就需要在两者之间架起一座桥梁。

本文将手把手教你如何使用 Spring Boot 构建一个“适配器层”,将传统的 SOAP 服务封装并转换为现代的 RESTful 接口。

🏗️ 核心架构思路

我们的目标不是重写 SOAP 服务,而是创建一个代理层(Adapter Layer)

  1. REST 控制器:接收前端的 JSON 请求。
  2. 转换逻辑:将 JSON 参数转换为 SOAP 请求对象。
  3. SOAP 客户端:调用旧的 Web Service。
  4. 响应映射:将 SOAP 返回的 XML 对象转换为 JSON DTO 返回给前端。

🛠️ 第一步:引入依赖

pom.xml 中,我们需要引入 Spring Web(用于 REST)和 Spring Web Services(用于 SOAP 客户端支持)。

<dependencies>
    <!-- 用于开发 REST 接口 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 用于调用 SOAP 服务 (JAX-WS 支持) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web-services</artifactId>
    </dependency>
    
    <!-- Lombok (可选,简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

在实际项目中,SOAP 的类通常是根据 WSDL 自动生成的。为了演示,我们手动模拟两个类:一个是 SOAP 服务端返回的复杂对象,一个是 REST 客户端想要的简洁对象。

1. 模拟 SOAP 服务端返回的对象

package com.example.soapclient;

import jakarta.xml.bind.annotation.*;

// 模拟 SOAP 响应中的复杂对象
@XmlRootElement(name = "UserResponse", namespace = "http://example.com/soap")
public class SoapUserResponse {
    private String userId;
    private String fullName;
    private String emailAddress;

    // 构造函数、Getter 和 Setter 省略 (实际开发中由 IDE 生成或 Lombok)
    public SoapUserResponse() {}
    public SoapUserResponse(String userId, String fullName, String emailAddress) {
        this.userId = userId;
        this.fullName = fullName;
        this.emailAddress = emailAddress;
    }
    // ... getters and setters
    public String getUserId() { return userId; }
    public String getFullName() { return fullName; }
    public String getEmailAddress() { return emailAddress; }
}

2. 定义 REST 接口返回的 DTO

package com.example.restapi;

// REST 客户端接收的简洁 JSON 对象
public class UserDTO {
    private String id;
    private String name;
    // 注意:SOAP 中有 email,但 REST 接口决定不暴露这个字段,实现数据脱敏

    public UserDTO() {}
    public UserDTO(String id, String name) {
        this.id = id;
        this.name = name;
    }
    // ... getters and setters
    public String getId() { return id; }
    public String getName() { return name; }
}

这个组件负责真正去调用旧的 Web Service。在实际场景中,这里会使用 WebServiceTemplate 进行 XML 通信。

package com.example.soapclient;

import org.springframework.stereotype.Component;
import org.springframework.ws.client.core.WebServiceTemplate;

@Component
public class LegacySoapClient {

    private final WebServiceTemplate webServiceTemplate;

    public LegacySoapClient(WebServiceTemplate webServiceTemplate) {
        this.webServiceTemplate = webServiceTemplate;
    }

    /**
     * 模拟调用远程 SOAP 服务获取用户信息
     */
    public SoapUserResponse getUserFromSoapService(String userId) {
        System.out.println(">>> 正在调用 SOAP 服务,获取用户 ID: " + userId);
        
        // 【实际代码示例】:
        // return (SoapUserResponse) webServiceTemplate.marshalSendAndReceive(
        //     "http://localhost:8080/soap-service", 
        //     new GetUserRequest(userId)
        // );

        // 这里直接返回一个模拟的 SOAP 响应对象用于演示
        return new SoapUserResponse(userId, "张三 (来自 SOAP)", "zhangsan@old-system.com");
    }
}

这是最关键的一步。我们创建一个标准的 Spring MVC 控制器,在内部注入 SOAP 客户端,完成“翻译”工作。

package com.example.restapi;

import com.example.soapclient.LegacySoapClient;
import com.example.soapclient.SoapUserResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/users")
public class UserRestAdapter {

    private final LegacySoapClient legacySoapClient;

    // 注入 SOAP 客户端
    public UserRestAdapter(LegacySoapClient legacySoapClient) {
        this.legacySoapClient = legacySoapClient;
    }

    /**
     * REST 接口: GET /api/v1/users/{id}
     * 内部逻辑: 接收 REST 请求 -> 调用 SOAP -> 转换数据 -> 返回 JSON
     */
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUser(@PathVariable String id) {
        
        // 1. [调用] 委托给 SOAP 客户端
        SoapUserResponse soapResponse = legacySoapClient.getUserFromSoapService(id);

        // 2. [转换] SOAP 响应对象 -> REST DTO
        UserDTO userDTO = convertToDTO(soapResponse);

        // 3. [响应] 返回 JSON
        return ResponseEntity.ok(userDTO);
    }
    
    /**
     * 数据映射方法:负责字段过滤和重命名
     */
    private UserDTO convertToDTO(SoapUserResponse soapUser) {
        UserDTO dto = new UserDTO();
        dto.setId(soapUser.getUserId());
        dto.setName(soapUser.getFullName());
        // 故意忽略 emailAddress 字段,实现字段过滤
        return dto;
    }
}

⚙️ 第五步:配置类

为了让 WebServiceTemplate 能够处理 XML 和 Java 对象的转换,我们需要配置一个 Marshaller。

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;

@Configuration
public class SoapConfig {

    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        // 指定包含 JAXB 注解(@XmlRootElement)的包路径
        marshaller.setPackagesToScan("com.example.soapclient");
        return marshaller;
    }

    @Bean
    public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
        WebServiceTemplate template = new WebServiceTemplate();
        template.setMarshaller(marshaller);
        template.setUnmarshaller(marshaller);
        return template;
    }
}

🧪 测试与验证

启动 Spring Boot 应用后,我们可以使用 Postman 或 curl 进行测试。

请求:

GET http://localhost:8080/api/v1/users/1001

控制台日志:

>>> 正在调用 SOAP 服务,获取用户 ID: 1001

响应结果 (JSON):

{
  "id": "1001",
  "name": "张三 (来自 SOAP)"
}

通过这种方式,我们成功实现了:

  • 协议解耦:前端不再依赖复杂的 SOAP XML,而是使用轻量级的 JSON。
  • 接口现代化:将 SOAP 的过程式调用(通常只有 POST)转换为了符合 REST 风格的资源操作(GET /users/{id})。
  • 数据脱敏:在 convertToDTO 方法中,我们可以灵活地隐藏 SOAP 返回的敏感字段(如 email),只暴露必要信息。

这种“适配器模式”是遗留系统重构中最稳妥、最常用的手段之一。

0人推荐
随时随地看视频
慕课网APP