List.of( )序列化问题

1,040 阅读1分钟

背景

在某个业务场景下,需要拿code值去查询对应的数据。下游提供接口提供参数是个codeList,所以直接List.of(code)传给了下游。 在RPC接口调试过程中发现HessianFieldException的异常,提示java.util.List cannot be assigned from null。

PixPin_2024-11-22_14-30-57.png

排查和问题原因

从报错中可以看到是Hessian2Input.readObject()抛出的问题,看到readObject()方法猜测可能是序列化问题,然后跟了下List.of()的源码。

  1. List.of 的响应是ImmutableCollections.listFromTrustedArray 返回的ListN类。
    static <E> List<E> of(E e1, E e2, E e3) {
        return ImmutableCollections.listFromTrustedArray(e1, e2, e3);
    }

    static <E> List<E> listFromTrustedArray(Object... input) {
        assert input.getClass() == Object[].class;
        for (Object o : input) { // implicit null check of 'input' array
            Objects.requireNonNull(o);
        }

        return switch (input.length) {
            case 0  -> (List<E>) ImmutableCollections.EMPTY_LIST;
            case 1  -> (List<E>) new List12<>(input[0]);
            case 2  -> (List<E>) new List12<>(input[0], input[1]);
            default -> (List<E>) new ListN<>(input, false);
        };
    }
  1. 然后进入看一下ListN类的数据结构,很简单一个elements数组存储数据和一个allowNulls是否允许为null的标记位。其中序列化方法writeReplace返回了个CollSer类
        @Stable
        private final E[] elements;

        @Stable
        private final boolean allowNulls;

        // caller must ensure that elements has no nulls if allowNulls is false
        private ListN(E[] elements, boolean allowNulls) {
            this.elements = elements;
            this.allowNulls = allowNulls;
        }

        @java.io.Serial
        private Object writeReplace() {
        return new CollSer(allowNulls ? CollSer.IMM_LIST_NULLS : CollSer.IMM_LIST, elements);
        }

3.我们进入Collser就可以发现问题的原因,CollSer将存储数据赋给array字段。但是array字段是被transient修饰的,在序列化过程中是被忽略的。所以在序列化后变成了null值。


    private final int tag;
    /**
     * @serial
     * @since 9
     */
    private transient Object[] array;

    CollSer(int t, Object... a) {
        tag = t;
        array = a;
    }