杰瑞科技汇

Java WebService如何传递数组?

我们将主要围绕最常用的标准 JAX-WS (Java API for XML-Based Web Services) 来展开,并简要提及 JAX-RS (Java API for RESTful Web Services)

Java WebService如何传递数组?-图1
(图片来源网络,侵删)

核心概念:WebService 如何传输数组?

本质上,WebService(尤其是基于 SOAP 的)通过 XML 来传输数据,Java 数组在 XML 中通常被表示为一个列表数组结构,最常见的形式是:

<ns2:myArray xmlns:ns2="http://ws.example.com/">
  <item>元素1</item>
  <item>元素2</item>
  <item>元素3</item>
</ns2:myArray>

或者,在更复杂的场景下(数组元素本身是复杂对象),它可能会被包装成一个带有特定命名空间的列表。


JAX-WS 处理数组

JAX-WS 是 Java 中创建和消费 SOAP WebService 的标准 API,处理数组非常直接。

WebService 方法接收和返回基本类型数组

这是最简单的情况,你只需要在 Java 方法中直接使用 int[], String[], double[] 等即可。

Java WebService如何传递数组?-图2
(图片来源网络,侵删)

创建 WebService 接口 (SEI - Service Endpoint Interface)

import javax.jws.WebService;
import javax.jws.WebMethod;
// 使用 @WebService 注解标记这是一个 WebService 接口
@WebService
public interface ArrayWebService {
    // 接收一个字符串数组,返回一个整数数组(字符串的长度)
    @WebMethod
    int[] getStringLengths(String[] stringArray);
    // 接收一个整数数组,返回一个新的数组,每个元素都乘以2
    @WebMethod
    int[] multiplyByTwo(int[] numbers);
}

实现 WebService 接口

import javax.jws.WebService;
@WebService(endpointInterface = "com.example.ArrayWebService") // 指定接口
public class ArrayWebServiceImpl implements ArrayWebService {
    @Override
    public int[] getStringLengths(String[] stringArray) {
        if (stringArray == null) {
            return new int[0]; // 返回空数组
        }
        int[] lengths = new int[stringArray.length];
        for (int i = 0; i < stringArray.length; i++) {
            lengths[i] = stringArray[i] != null ? stringArray[i].length() : 0;
        }
        return lengths;
    }
    @Override
    public int[] multiplyByTwo(int[] numbers) {
        if (numbers == null) {
            return new int[0];
        }
        int[] result = new int[numbers.length];
        for (int i = 0; i < numbers.length; i++) {
            result[i] = numbers[i] * 2;
        }
        return result;
    }
}

发布 WebService

你可以使用 JAX-WS 内置的 API 来发布这个服务。

Java WebService如何传递数组?-图3
(图片来源网络,侵删)
import javax.xml.ws.Endpoint;
public class ArrayPublisher {
    public static void main(String[] args) {
        // 创建服务实现类的实例
        ArrayWebServiceImpl service = new ArrayWebServiceImpl();
        // 发布服务到指定地址
        // http://localhost:8080/ArrayWebService 是服务的访问地址
        Endpoint.publish("http://localhost:8080/ArrayWebService", service);
        System.out.println("WebService 已发布在 http://localhost:8080/ArrayWebService");
    }
}

客户端调用

客户端可以通过 wsimport 工具生成客户端代码,然后像调用本地方法一样调用。

在命令行执行:

wsimport -p com.example.client -keep http://localhost:8080/ArrayWebService?wsdl

这会生成一堆客户端辅助类。

客户端代码可以这样写:

import com.example.client.ArrayWebService;
import com.example.client.ArrayWebServiceService;
public class ArrayClient {
    public static void main(String[] args) {
        // 创建服务服务视图
        ArrayWebServiceService service = new ArrayWebServiceService();
        // 获取 WebService 端口
        ArrayWebService webService = service.getArrayWebServicePort();
        // 调用方法
        String[] names = {"Alice", "Bob", "Charlie"};
        int[] lengths = webService.getStringLengths(names);
        System.out.println("字符串长度数组: ");
        for (int len : lengths) {
            System.out.print(len + " "); // 输出: 5 3 7
        }
        System.out.println("\n");
        int[] numbers = {1, 2, 3, 4, 5};
        int[] doubled = webService.multiplyByTwo(numbers);
        System.out.println("乘以2后的数组: ");
        for (int num : doubled) {
            System.out.print(num + " "); // 输出: 2 4 6 8 10
        }
    }
}

WebService 方法接收和返回自定义对象数组

当数组元素是复杂的 Java 对象时,JAX-WS 会自动处理这些对象的序列化和反序列化。

定义自定义对象

import javax.xml.bind.annotation.XmlRootElement;
// @XmlRootElement 是必须的,它告诉 JAXB 如何将这个类映射到 XML 根元素
@XmlRootElement
public class User {
    private String name;
    private int age;
    private String email;
    // 必须有无参构造函数
    public User() {}
    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    // Getter 和 Setter 是必须的
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

在 WebService 接口中使用对象数组

import javax.jws.WebService;
import javax.jws.WebMethod;
import java.util.List; // JAX-WS 也支持 List
@WebService
public interface UserWebService {
    @WebMethod
    User[] createUsers();
    @WebMethod
    String processUsers(User[] users);
}

实现类

import javax.jws.WebService;
@WebService(endpointInterface = "com.example.UserWebService")
public class UserWebServiceImpl implements UserWebService {
    @Override
    public User[] createUsers() {
        User[] users = new User[2];
        users[0] = new User("John Doe", 30, "john.doe@example.com");
        users[1] = new User("Jane Smith", 25, "jane.smith@example.com");
        return users;
    }
    @Override
    public String processUsers(User[] users) {
        if (users == null || users.length == 0) {
            return "用户列表为空。";
        }
        StringBuilder sb = new StringBuilder("处理了 " + users.length + " 个用户:\n");
        for (User user : users) {
            sb.append("- ").append(user.getName()).append(", 年龄: ").append(user.getAge()).append("\n");
        }
        return sb.toString();
    }
}

发布和客户端调用方式与前面类似。wsimport 会自动为 User 类生成对应的客户端类。


JAX-RS (RESTful WebService) 处理数组

对于 RESTful WebService (通常基于 JSON),处理数组的方式与 JAX-WS 不同,因为它不使用 SOAP 和 WSDL,而是直接处理 HTTP 请求和响应体(通常是 JSON)。

添加依赖

你需要一个 JAX-RS 实现,Jersey 或 RESTEasy,以 Jersey 为例(Maven 依赖):

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>3.1.3</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>3.1.3</version>
</dependency>

创建资源类

使用 @Path, @POST, @GET, @Consumes, @Produces 等注解。

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Arrays;
import java.util.List;
@Path("/array-resource")
public class ArrayResource {
    // 接收一个 JSON 数组,并返回处理后的文本
    @POST
    @Path("/process")
    @Consumes(MediaType.APPLICATION_JSON) // 指定接收 JSON 格式的数组
    @Produces(MediaType.TEXT_PLAIN)
    public String processIntegerArray(int[] numbers) {
        if (numbers == null) {
            return "接收到的数组为 null";
        }
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return "接收到的数组是: " + Arrays.toString(numbers) + ",它们的和是: " + sum;
    }
    // 返回一个 JSON 数组
    @GET
    @Path("/get-names")
    @Produces(MediaType.APPLICATION_JSON)
    public String[] getNames() {
        return new String[]{"Alice", "Bob", "Charlie"};
    }
    // 接收一个 JSON 对象数组
    @POST
    @Path("/add-users")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public List<User> addUsers(List<User> users) {
        // 在实际应用中,这里会把 users 保存到数据库
        System.out.println("服务器端收到用户列表: " + users.size());
        // 返回接收到的用户列表作为确认
        return users;
    }
}

客户端调用 (使用 cURL)

# 调用返回数组的方法
curl -X GET "http://localhost:8080/your-app-context/array-resource/get-names" -H "Accept: application/json"
# 预期响应: ["Alice","Bob","Charlie"]
# 调用接收数组的方法
curl -X POST "http://localhost:8080/your-app-context/array-resource/process" \
-H "Content-Type: application/json" \
-d '[10, 20, 30]'
# 预期响应: 接收到的数组是: [10, 20, 30],它们的和是: 60
# 调用接收对象数组的方法
curl -X POST "http://localhost:8080/your-app-context/array-resource/add-users" \
-H "Content-Type: application/json" \
-d '[{"name":"John","age":30,"email":"john@example.com"}, {"name":"Jane","age":25,"email":"jane@example.com"}]'
# 预期响应: [{"name":"John","age":30,"email":"john@example.com"}, {"name":"Jane","age":25,"email":"jane@example.com"}]

重要注意事项和最佳实践

  1. 空数组 vs. null

    • 最佳实践: 在代码中明确区分空数组 (new Type[0]) 和 null,返回一个空数组比返回 null 更安全,可以避免客户端出现 NullPointerException,在服务端,如果输入为 null,可以优雅地处理(如返回空数组或抛出明确的业务异常)。
  2. 性能和大数据量

    • 传输非常大的数组时,可能会遇到内存问题(OutOfMemoryError)或性能瓶颈。
    • 解决方案:
      • 分页/分块: 不要一次性传输整个数组,而是分页或分块传输,提供 offsetlimit 参数。
      • 流式处理: 对于文件等大数据,可以考虑流式传输,而不是将整个文件读入内存再作为数组发送,JAX-WS 和 JAX-RS 都支持流式处理。
  3. 复杂对象数组的注解

    • 在 JAX-WS 中,@XmlRootElement 通常足够了,但如果你的类无法修改,或者需要更精细的控制,可以使用 @XmlType@XmlElement 等 JAXB 注解来定义 XML 映射规则。
    • 如果你想改变 XML 元素的名称:
      @XmlRootElement(name = "user-details")
      @XmlType(name = "UserType")
      public class User {
          @XmlElement(name = "full-name")
          private String name;
          // ...
      }
  4. JAX-RS 中的 List vs. 数组

    • 在 JAX-RS 中,List<T>T[] 通常都可以用作方法的参数或返回类型,框架会自动将 JSON 数组反序列化为 List 或数组,使用 List 通常更灵活,尤其是在处理动态大小的数据时。
特性 JAX-WS (SOAP) JAX-RS (REST/JSON)
核心协议 SOAP (XML) HTTP (JSON/XML)
API @WebService, @WebMethod @Path, @GET, @POST, @Consumes, @Produces
数组表示 XML 列表 (<item>...</item>) JSON 数组 ([...])
客户端生成 wsimport 工具 手动构建 HTTP 请求 或使用 Jersey Client API
处理复杂对象 依赖 JAXB (@XmlRootElement) 依赖 JSON-B/Jackson/Gson (通常自动处理)
适用场景 企业级、需要契约、安全性要求高的服务 公开 API、移动端、前后端分离应用

选择哪种技术取决于你的项目需求,对于需要严格契约和 WS-* 标准的企业集成,JAX-WS 是一个强大的选择,对于现代、轻量级的 Web API,JAX-RS 是事实上的标准。

分享:
扫描分享到社交APP
上一篇
下一篇