I. Problem description

When the service is started, the interface is called normally. However, after running for some time, when the interface is called again, the following exceptions will be found:

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

See above exception message is the Test classThe default constructor could not be foundAfter a while, the same problem will occur again. Very weird. The definition of the Test class is as follows:

data class Test(val name: String)
Copy the code

As you can see, the Test class does not have a default constructor. It seems natural to throw the above exception, but there are two problems:

  1. Why is deserialization normal at first?
  2. What causes subsequent deserialization to fail?

Second, answer the question

From the above exception information, you can find the code segment that threw the exception, as follows:

Class: com. Alibaba. Fastjson. Util. JavaBeanInfoif(paramNames ! =null&& types.length == paramnames.length) {code omitted here} esle {throw new JSONException("default constructor not found. " + clazz);
}
Copy the code

As can be seen from the above logic, as long asparamNamesfornulltypes.length ! = paramNames.lengthI’m going to throw an exception. Follow up and find it hereparamNamesWhere the value is assigned, as shown below:Into theTypeUtils.getKoltinConstructorParametersThis method finds all the returnsnullAfter code simplification, the following request:

Class: com. Alibaba. Fastjson. Util. TypeUtilspublicThe static String [] getKoltinConstructorParameters (Class clazz) {code omitted hereif (kotlin_kclass_constructor == null) {return null; } the code is omitted hereif (kotlin_error){
    return null;
  }
  
  try{code omitted here}catch(Throwable e){
     e.printStackTrace();
     kotlin_error = true;
  }
    
  return null;
}
Copy the code

As you can see from the code above, there are three cases where a path returns NULL:

  1. kotlin_kclass_constructorfornull
  2. kotlin_errorfortrue
  3. Return at last when none of the previous normal cases had returnednull

One of the more suspicious bits is kotlin_error, and find its definition

private static volatile boolean kotlin_error;
Copy the code

Kotlin_error is a private static variable!! And once it is set to true, there is no place to change it to false. That’s basically it. The logic in the try above is wrong. It just prints the stack and marks kotlin_error as true. The background all adjustable TypeUtils getKoltinConstructorParameters method will return null. This leads directly to the default constructor not found we initially saw. The exception. Here we find the answers to the above two questions:

  1. inkotlin_errorIt’s not marked astrueIs normal
  2. kotlin_errorOnce marked asfalseThe subsequent deserialization will be abnormal

Why is kotlin_error marked true

So, whykotlin_errorIt’s going to be labeledtrue? We find the log information for printing the stack, as shown below:In the originalTypeUtils.getKoltinConstructorParametersMethod throws an NPE exception. Find the log corresponding to the previous request interface and find that the return value of the interface has a variable assignedUnitwhenUnitType this method raises an NPE exception, as shown in the code comment below:

try{
  Object constructor = null;
  // clazz is class kotlin.unit
  Object kclassImpl = kotlin_kclass_constructor.newInstance(clazz);
  // Kotlin. Unit has no constructor, so there are no elements in 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;
  }
  // Since there are no elements in it that have not been assigned to the above for loop, constructor remains null and the invoke method below throws an NPE exception (the following line has the number of lines: 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();
// Kotlin_error is set to true after an NPE exception is raised above
  kotlin_error = true;
}
Copy the code