fastjson+kotlin一次序列化异常后导致之前本来正常反序列化失败

2,108 阅读3分钟

一、问题描述

服务刚启动时调用接口都是正常的,但是运行一段时间后,再调用接口时就会发现以下异常:

Method threw 'com.alibaba.fastjson.JSONException' exception: default constructor not found. XXX

看到上面异常信息是Test类找不到默认构造函数,再重启服务一切又正常了,过一段时间又会出现同样的问题,非常诡异,Test类的定义如下:

data class Test(val name: String)

可以看到Test类确实是没有默认构造函数,抛出上面的异常貌似是理所当然的,但是有两个疑问点:

  1. 为什么刚开始反序列化是正常的?
  2. 是什么原因导致后面反序列化一直失败?

二、疑点解答

通过上面的异常信息,找到抛出异常的代码段,精简后如下所示:

类:com.alibaba.fastjson.util.JavaBeanInfo

if (paramNames != null && types.length == paramNames.length) {

  此处代码省略
  
} esle {
  throw new JSONException("default constructor not found. " + clazz);
}

由上面的判断逻辑可以看到,只要paramNamesnulltypes.length != paramNames.length 就会抛异常了。再往前跟进找到给paramNames赋值的地方,如下图所示: 进到TypeUtils.getKoltinConstructorParameters这个方法,找到所有返回null的路径,代码精简后如下所求:

类:com.alibaba.fastjson.util.TypeUtils

public static String[] getKoltinConstructorParameters(Class clazz){

    此处代码省略

  if (kotlin_kclass_constructor == null){
    return null;
  }

    此处代码省略

  if (kotlin_error){
    return null;
  }
  
  try{
  
    此处代码省略
    
  } catch(Throwable e){
     e.printStackTrace();
     kotlin_error = true;
  }
    
  return null;
}

由上面的代码可以看到,返回null的路径有三种情况:

  1. kotlin_kclass_constructornull
  2. kotlin_errortrue
  3. 之前所有的正常情况都没有返回的情况下,最后返回null

其中比较可疑的是kotlin_error这个标记位,再找到它的定义

private static volatile boolean kotlin_error;

kotlin_error是一个私有的静态变量!!!而且一旦设置为true,再也没有地方将其更改变为false。这样就基本确定了,应该是上面的try所包含的逻辑出现了问题,它只打印了一下堆栈信息,就把kotlin_error标记成了true,后台所有调TypeUtils.getKoltinConstructorParameters方法都会返回null。这样就会直接导致我们最初看到的default constructor not found.异常。到此我们就找到上面两个疑问点的答案:

  1. kotlin_error没有被标记为true时都是正常的
  2. kotlin_error一旦被标记为false后面的反序列化就会异常

三、kotlin_error 为什么会被标记为 true 呢

那么,至于为什么kotlin_error会被标记为true呢?我们找到打印堆栈的日志信息,如下图所示: 原来在TypeUtils.getKoltinConstructorParameters的方法中抛了一个NPE的异常。找到日志对应之前的请求接口,发现接口的返回值中有一个变量被赋值成了Unit,当Unit类型执行此方法就会抛出NPE的异常,详见下面代码注释:

try{
  Object constructor = null;
  // 此时 clazz 为 class kotlin.Unit
  Object kclassImpl = kotlin_kclass_constructor.newInstance(clazz);
  // kotlin.Unit 没有构造函数,所以 it 中没有元素
  Iterable it = (Iterable) kotlin_kclass_getConstructors.invoke(kclassImpl);
  for(Iterator iterator = it.iterator(); iterator.hasNext(); iterator.hasNext()){
      Object item = iterator.next();
      List parameters = (List) kotlin_kfunction_getParameters.invoke(item);
      if (constructor != null && parameters.size() == 0) {
          continue;
      }
      constructor = item;
  }
  // 因为 it 中没有元素没有进入上面的for循环中赋值,所以constructor还是为null,在下面invoke方法就会抛出NPE的异常(下面这一行的行数为:2862)
  List parameters = (List) kotlin_kfunction_getParameters.invoke(constructor);
  String[] names = new String[parameters.size()];
  for(int i = 0; i < parameters.size(); i++){
      Object param = parameters.get(i);
      names[i] = (String) kotlin_kparameter_getName.invoke(param);
  }
  return names;
} catch(Throwable e){
  e.printStackTrace();
//上面抛出NPE异常后,就会将kotlin_error设置为true
  kotlin_error = true;
}