1. 概述

之前我们已经演示过如何使用 Spring 构建一个 SOAP Web 服务

本文将重点讲解如何基于 Spring 构建一个客户端来消费这个 SOAP 服务

我们曾在另一篇文章 Java 中调用 SOAP Web 服务 中使用 JAX-WS RI 实现过类似功能。而本篇则聚焦于 Spring WS 的方式,更加契合 Spring 生态,集成也更简洁。


2. 回顾:Spring 中的 SOAP Web 服务

在前面的教程中,我们基于 Spring 实现了一个根据国家名称查询国家信息的 Web 服务。在进入客户端开发之前,先快速回顾一下服务端的关键结构。

我们采用的是 契约优先(Contract-First) 的开发方式:

  1. ✅ 编写 XSD 文件定义数据模型和接口契约
  2. ✅ 使用 jaxb2-maven-plugin 根据 XSD 自动生成 Java 类(请求、响应、实体)
  3. ✅ 实现四个核心类:
    • CountryEndpoint:处理请求的接口实现
    • CountryRepository:提供数据访问
    • WebServiceConfig:配置 Spring WS 所需的 Bean
    • Application:Spring Boot 启动类

最后通过 cURL 发送 SOAP 请求完成了测试。

现在我们只需启动该服务(运行 Spring Boot 应用),就可以开始构建客户端了。


3. 客户端实现

接下来我们将 构建一个 Spring 客户端来调用并测试上述 Web 服务

整个过程可以分为以下几个步骤。

3.1 生成客户端代码

首先,我们需要根据服务端提供的 WSDL 自动生成客户端所需的 Java 类。WSDL 地址如下:

http://localhost:8080/ws/countries.wsdl

我们将这个 WSDL 文件下载并保存到项目的 src/main/resources 目录下。

然后,在 pom.xml 中添加 maven-jaxb2-plugin 插件来自动生成代码:

<plugin> 
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.15.3</version>
    <executions>
         <execution>
              <goals>
                  <goal>generate</goal>
              </goals>
         </execution>
    </executions>
    <configuration>
          <schemaLanguage>WSDL</schemaLanguage>
          <generateDirectory>${project.basedir}/src/main/java</generateDirectory>
          <generatePackage>com.baeldung.springsoap.client.gen</generatePackage>
          <schemaDirectory>${project.basedir}/src/main/resources</schemaDirectory>
          <schemaIncludes>
             <include>countries.wsdl</include>
          </schemaIncludes>
    </configuration>
</plugin>

⚠️ 插件配置说明:

  • generateDirectory:生成类的输出目录
  • generatePackage:生成类所属的包名
  • schemaDirectoryschemaIncludes:指定 WSDL 文件路径和名称

执行以下命令触发代码生成:

mvn compile

✅ 生成的类与服务端基本一致,主要包括:

  • Country.java, Currency.java:数据模型 POJO
  • GetCountryRequest.java:请求对象
  • GetCountryResponse.java:响应对象

这就是契约优先的好处——只要拿到 WSDL,客户端就能生成完全匹配的类型,不怕对接出错。


3.2 CountryClient:封装调用逻辑

接下来我们需要创建一个客户端类,继承 Spring 提供的 WebServiceGatewaySupport,以便使用其内置的 WebServiceTemplate

定义 CountryClient 类如下:

public class CountryClient extends WebServiceGatewaySupport {

    public GetCountryResponse getCountry(String country) {
        GetCountryRequest request = new GetCountryRequest();
        request.setName(country);

        GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate()
          .marshalSendAndReceive(request);
        return response;
    }
}

📌 关键点解析:

  • getCountry() 方法对应服务端暴露的操作
  • ✅ 创建 GetCountryRequest 并设置参数
  • ✅ 调用 marshalSendAndReceive() 完成 SOAP 请求/响应交换
  • ✅ 返回反序列化后的 GetCountryResponse

底层的 XML 序列化/反序列化由 Marshaller 自动完成,我们无需手动处理 SOAP 消息体。


3.3 CountryClientConfig:配置客户端 Bean

为了让 CountryClient 正常工作,我们需要配置两个核心 Bean:

  1. Jaxb2Marshaller:负责 Java 对象与 XML 之间的转换
  2. CountryClient:封装调用逻辑的客户端实例

配置类如下:

@Configuration
public class CountryClientConfig {

    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath("com.baeldung.springsoap.client.gen");
        return marshaller;
    }

    @Bean
    public CountryClient countryClient(Jaxb2Marshaller marshaller) {
        CountryClient client = new CountryClient();
        client.setDefaultUri("http://localhost:8080/ws");
        client.setMarshaller(marshaller);
        client.setUnmarshaller(marshaller);
        return client;
    }
}

⚠️ 注意事项:

  • marshaller.setContextPath() 必须与 pom.xmlgeneratePackage 一致
  • setDefaultUri() 设置的是服务端的 SOAP 接口地址,对应 WSDL 中的 <soap:address location="..."/>
  • 同时设置 marshallerunmarshaller,确保收发都能正常处理

4. 测试客户端功能

使用 JUnit 编写集成测试,验证客户端是否能正确调用远程服务。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CountryClientConfig.class, loader = AnnotationConfigContextLoader.class)
public class ClientLiveTest {

    @Autowired
    CountryClient client;

    @Test
    public void givenCountryService_whenCountryPoland_thenCapitalIsWarsaw() {
        GetCountryResponse response = client.getCountry("Poland");
        assertEquals("Warsaw", response.getCountry().getCapital());
    }

    @Test
    public void givenCountryService_whenCountrySpain_thenCurrencyEUR() {
        GetCountryResponse response = client.getCountry("Spain");
        assertEquals(Currency.EUR, response.getCountry().getCurrency());
    }
}

✅ 测试要点:

  • 使用 @ContextConfiguration 加载 CountryClientConfig 配置类
  • 自动注入 CountryClient 实例
  • 调用 getCountry() 获取响应
  • 利用生成的 POJO(如 Country, Currency)进行断言,类型安全且直观

运行测试前请确保服务端已启动,否则会报连接超时 ❌。


5. 总结

本文演示了如何使用 Spring WS 构建一个 简洁、类型安全的 SOAP 客户端

核心流程总结如下:

  1. ✅ 获取 WSDL 文件
  2. ✅ 使用 maven-jaxb2-plugin 自动生成客户端类
  3. ✅ 继承 WebServiceGatewaySupport 封装调用逻辑
  4. ✅ 配置 Jaxb2Marshaller 和客户端 Bean
  5. ✅ 编写测试验证功能

虽然我们只实现了最基础的调用,但 Spring WS 的能力远不止于此。比如它还支持:

  • 拦截器(Interceptors)
  • 错误处理(Fault Handling)
  • 异步调用
  • 安全(Spring Security 集成)

感兴趣的同学可以进一步查阅官方文档:Spring WS Reference

✅ 源码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/spring-soap
建议集合,以后对接老系统踩坑时可以直接参考。


原始标题:Invoking a SOAP Web Service in Spring | Baeldung