背景
在某个业务场景下,需要拿code值去查询对应的数据。下游提供接口提供参数是个codeList,所以直接List.of(code)传给了下游。 在RPC接口调试过程中发现HessianFieldException的异常,提示java.util.List cannot be assigned from null。
排查和问题原因
从报错中可以看到是Hessian2Input.readObject()抛出的问题,看到readObject()方法猜测可能是序列化问题,然后跟了下List.of()的源码。
- 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);
};
}
- 然后进入看一下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;
}