杰瑞科技汇

Java客户端如何调用WebService?

我们将从最核心、最主流的两种技术栈入手:

Java客户端如何调用WebService?-图1
(图片来源网络,侵删)
  1. JAX-WS (Java API for XML Web Services):Java 官方标准,用于创建基于 SOAP (Simple Object Access Protocol) 协议的 WebService,这是目前企业级应用中最常用的技术。
  2. JAX-RS (Java API for RESTful Web Services):Java 官方标准,用于创建基于 REST (Representational State Transfer) 架构风格的 WebService,现在非常流行,尤其适用于移动端和前后端分离的项目。

核心概念:SOAP vs. REST

在开始之前,理解这两种风格的区别非常重要:

特性 SOAP (Simple Object Access Protocol) REST (Representational State Transfer)
协议 严格基于 XML,必须通过 SOAP 协议在 HTTP 上传输。 基于 HTTP 协议本身,可以使用 JSON、XML、Text 等多种数据格式。
标准 标准严格,有 WSDL (Web Services Description Language) 文件来定义服务接口。 无严格标准,更偏向一种架构风格,依赖 URI 来标识资源。
数据格式 主要是 XML。 主要是 JSON,也支持 XML、HTML 等。
使用场景 企业级应用、金融、电信等对安全性、事务性要求高的场景。 公开 API、移动应用、前后端分离项目。
复杂度 较高,配置和解析 XML 较为繁琐。 较低,更轻量、更易于理解和使用。

JAX-WS 客户端 (针对 SOAP WebService)

JAX-WS 是 Java 平台的标准 API,我们通常不需要直接引入第三方库,因为它们已经包含在 JDK 和 Java EE 中。

创建客户端的几种方式

使用 wsimport 工具生成客户端代码(最经典、最常用)

这是最传统和可靠的方法。wsimport 是 JDK 自带的一个命令行工具,它会根据 WSDL 文件自动生成一系列 Java 类(客户端存根/骨架),这些类封装了与服务器通信的细节。

Java客户端如何调用WebService?-图2
(图片来源网络,侵删)

步骤:

  1. 获取 WSDL 文件 任何一个 SOAP WebService 都会提供一个 WSDL 文件,它描述了服务的所有接口、方法、参数和返回值,通常是一个 URL,http://www.example.com/service?wsdl

  2. 运行 wsimport 命令 打开命令行(CMD 或 PowerShell),执行以下命令:

    # 基本语法
    wsimport -p <生成的包名> -keep <wsdl文件的URL或路径>
    # 示例
    # 假设 WSDL 地址是:http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl
    # 我们想把生成的代码放在 com.example.weather 包下,并保留生成的源文件
    wsimport -p com.example.weather -keep http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl

    参数解释:

    • -p: 指定生成的 Java 类的包名。
    • -keep: 生成 .java 源文件,方便查看和调试。
    • -d: 指定编译后的 .class 文件存放目录。
    • -s: 指定 .java 源文件存放目录(如果与 -d 不同)。
    • <wsdl_url>: WSDL 文件的地址。
  3. 使用生成的代码 执行命令后,会在你指定的包下生成一堆类,其中最重要的是:

    • WeatherSoap (或类似名字):这是服务的接口,包含了 WSDL 中定义的所有方法。
    • WeatherSoapService (或类似名字):这是服务的工厂类,用于获取 WeatherSoap 接口的实例。

    Java 代码示例:

    import com.example.weather.Weather;
    import com.example.weather.WeatherSoap;
    import com.example.weather.WeatherSoapService;
    public class JaxWsClientExample {
        public static void main(String[] args) {
            try {
                // 1. 创建服务工厂实例
                WeatherSoapService weatherService = new WeatherSoapService();
                // 2. 从工厂获取服务接口的代理对象
                WeatherSoap weatherSoap = weatherService.getWeatherSoap();
                // 3. 调用接口方法,就像调用本地方法一样
                // 假设 WSDL 中定义了一个 GetCityWeatherByZIP 方法
                String zipCode = "10001";
                Weather weatherInfo = weatherSoap.GetCityWeatherByZIP(zipCode);
                // 4. 处理返回结果
                if (weatherInfo != null) {
                    System.out.println("城市: " + weatherInfo.getCity());
                    System.out.println("温度: " + weatherInfo.getTemperature());
                    System.out.println("天气状况: " + weatherInfo.getConditions());
                } else {
                    System.out.println("未能获取天气信息。");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

使用动态调用(无需生成代码)

如果你不想生成一堆静态代码,或者 WSDL 地址是动态的,可以使用 Dispatch API 或 Provider API 进行动态调用。

示例代码 (使用 Dispatch API):

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
import java.net.URL;
public class DynamicJaxWsClient {
    public static void main(String[] args) throws Exception {
        // 1. 定义 WSDL 地址和目标命名空间
        String wsdlUrl = "http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl";
        String namespaceUri = "http://ws.cdyne.com/WeatherWS/";
        String serviceName = "Weather";
        String portName = "WeatherSoap";
        // 2. 创建 Service 实例
        URL url = new URL(wsdlUrl);
        QName qname = new QName(namespaceUri, serviceName);
        Service service = Service.create(url, qname);
        // 3. 创建 Dispatch 实例
        // Dispatch 可以是 PAYLOAD 或 MESSAGE 级别
        Dispatch<SOAPMessage> dispatch = service.createDispatch(
                new QName(namespaceUri, portName),
                SOAPMessage.class,
                Service.Mode.PAYLOAD);
        // 4. 创建 SOAP 请求消息
        MessageFactory mf = MessageFactory.newInstance();
        SOAPMessage request = mf.createMessage();
        SOAPEnvelope envelope = request.getSOAPPart().getEnvelope();
        SOAPBody body = envelope.getBody();
        // 创建请求体内容 (根据具体服务的 SOAP 格式)
        // 这里以 GetCityWeatherByZIP 为例
        QName bodyName = new QName(namespaceUri, "GetCityWeatherByZIP");
        body.addBodyElement(bodyName).addAttribute(new QName("ZIP"), "10001");
        // 5. 发送请求并获取响应
        System.out.println("发送请求...");
        SOAPMessage response = dispatch.invoke(request);
        // 6. 处理响应
        System.out.println("收到响应:");
        response.writeTo(System.out);
    }
}

JAX-RS 客户端 (针对 REST WebService)

JAX-RS 本身是服务端规范,但其客户端 API 非常强大,最常用的实现是 JerseyApache CXF

准备工作 (以 Jersey 为例)

你需要添加 Jersey 客户端的依赖,如果你使用 Maven,在 pom.xml 中添加:

<dependencies>
    <!-- Jersey 核心客户端依赖 -->
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>2.39.1</version> <!-- 使用最新版本 -->
    </dependency>
    <!-- 支持 JSON 格式 -->
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>2.39.1</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-binding</artifactId>
        <version>2.39.1</version>
    </dependency>
</dependencies>

创建和使用客户端

JAX-RS 客户端的核心是 ClientWebTarget 对象。

步骤:

  1. 创建 Client 实例:客户端的入口。
  2. 创建 WebTarget 实例:指定目标资源的 URI。
  3. 构建请求:通过 WebTarget 的方法(如 get(), post(), put(), delete())来指定 HTTP 方法和可选的实体(请求体)。
  4. 发送请求并获取响应:调用 invoke()response() 方法。
  5. 处理响应:从响应对象中获取状态码、头部和实体(响应体)。

示例代码 (调用一个获取用户信息的 REST API):

假设有一个 API GET http://example.com/api/users/1,返回 JSON 格式的用户信息:{"id": 1, "name": "John Doe", "email": "john.doe@example.com"}

定义与 JSON 对应的 Java 实体类 (POJO):

import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement // 用于 Jersey 的 JSON 映射
public class User {
    private int id;
    private String name;
    private String email;
    // 必须有无参构造函数
    public User() {}
    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

编写客户端代码:

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class JaxRsClientExample {
    public static void main(String[] args) {
        // API 的基础 URL
        String apiBaseUri = "http://example.com/api";
        // 1. 创建 Client 实例
        Client client = ClientBuilder.newClient();
        try {
            // 2. 创建 WebTarget,指向特定的资源
            WebTarget target = client.target(apiBaseUri).path("users").path("1");
            // 3. 发送 GET 请求,并期望返回 JSON 格式的数据
            //    并将其直接映射为 User 对象
            User user = target
                    .request(MediaType.APPLICATION_JSON) // 设置 Accept 头
                    .get(User.class); // 直接获取并映射为 User 对象
            // 4. 处理结果
            System.out.println("成功获取用户信息:");
            System.out.println(user);
            // --- 另一种方式:使用 Response 对象获取更多信息 ---
            System.out.println("\n使用 Response 对象获取信息:");
            Response response = target.request().get();
            if (response.getStatus() == 200) {
                User userFromResponse = response.readEntity(User.class);
                System.out.println("从 Response 中获取的用户: " + userFromResponse);
            } else {
                System.out.println("请求失败,状态码: " + response.getStatus());
            }
            // 关闭响应,释放资源
            response.close();
        } finally {
            // 5. 关闭 Client,释放资源
            client.close();
        }
    }
}

实用工具与框架

除了标准的 JAX-WS/JAX-RS,还有一些强大的第三方库可以简化开发:

  1. Spring WebFlux WebClient (推荐)

    • 简介:Spring 5 引入的响应式、非阻塞式 HTTP 客户端,是 Spring Boot 3.x 中进行 HTTP 调用的首选,它非常强大、灵活,并且与 Spring 生态系统无缝集成。

    • 优点:链式调用、支持异步/响应式、类型安全、易于测试。

    • 示例

      // 在 Spring Boot 应用中
      @RestController
      public class MyController {
          private final WebClient webClient;
          public MyController(WebClient.Builder webClientBuilder) {
              this.webClient = webClientBuilder.baseUrl("http://example.com/api").build();
          }
          @GetMapping("/fetch-user")
          public Mono<User> fetchUser() {
              return this.webClient.get()
                      .uri("/users/1")
                      .retrieve() // 发出请求并获取响应
                      .bodyToMono(User.class); // 将响应体映射为 Mono<User>
          }
      }
  2. Apache HttpClient

    • 简介:一个非常成熟和强大的底层 HTTP 客户端库,它不特定于 WebService,可以用于任何 HTTP 通信。
    • 优点:高度可配置、性能优秀、功能全面。
    • 缺点:API 相对繁琐,需要手动处理序列化/反序列化(如使用 Jackson/Gson)。

总结与如何选择

场景 推荐技术 理由
企业内部系统,需要强事务、高安全性的 SOAP 服务 JAX-WS (wsimport) 标准稳定,与 Java EE/Jakarta EE 深度集成。
调用公开的 REST API,返回 JSON JAX-RS (Jersey/CXF) 或 Spring WebClient JAX-RS 是标准,WebClient 是 Spring 生态下的现代、更强大的选择。
需要最大灵活性和对底层 HTTP 协议的完全控制 Apache HttpClient 提供了最底层的访问,可以定制任何细节。
在 Spring Boot 项目中进行 HTTP 调用 Spring WebClient 与项目技术栈完美融合,代码更简洁,支持异步编程。

对于初学者,建议:

  • 如果是 SOAP,直接掌握 wsimport 的使用流程。
  • 如果是 REST,优先学习 Spring WebClient,如果项目不基于 Spring,再学习 JAX-RS (Jersey)
分享:
扫描分享到社交APP
上一篇
下一篇