niubility!即使JavaBean没有默认无参构造器,Fastjson也可以反序列化。- - - - 阿里Fastjson反序列化源码分析
niubility!即使JavaBean没有默认无参构造器,fastjson也可以反序列化。
看下面示例代码,User这个JavaBean不包含默认无参构造器。执行这段代码不仅不会像Jackson那样抛出“没有无参构造器”的异常,还能正常反序列化。
@Test
public void testFastjsonCoDec() {
String jsonString = JSON.toJSONString(new User(1L, "Eric"));
System.out.println(JSON.parseObject(jsonString, User.class));
}
@Data
@AllArgsConstructor
public static class User {
private Long id;
private String name;
}
本文我们来分析Fastjson反序列化对象的源码(Fastjson反序列化源码),我使用的版本是fastjson-1.2.60。从中,你会了解到其中蕴藏的技术内幕。
Fastjson反序列化-相关组件类(部分)及调用链路
JSON# parseObject |
→ | ↓ | ||||
DefaultJSONParser# parseObject |
→ | ↓ | ||||
ParserConfig# getDeserializer |
||||||
↓ | ||||||
ParserConfig# createJavaBeanDeserializer |
→ | ↓ | ||||
ASMDeserializerFactory# createJavaBeanDeserializer |
||||||
JavaBeanDeserializer# JavaBeanDeserializer |
||||||
↓ | ← | ← | ||||
← | JavaBeanDeserializer# deserialize |
源码走起来。↓
§1. 入口 com.alibaba.fastjson.JSON
// com.alibaba.fastjson.JSON
public static <T> T parseObject(String text, Class<T> clazz) {
return parseObject(text, clazz, new Feature[0]);
}
public static <T> T parseObject(String json, Class<T> clazz, Feature... features) {
return (T) parseObject(json, (Type) clazz, ParserConfig.global, null, DEFAULT_PARSER_FEATURE, features);
}
注意第2个parseObject重载,其中指定的ParserConfig.global
是com.alibaba.fastjson.parser.ParserConfig
类的静态单例对象(饿汉式),Fastjson反序列化的重点就是这个对象的getDeserializer(java.lang.reflect.Type)
方法。暂且按下不表,我们先追踪调用到这个方法所经过的链路。
顺着上面的parseObject重载,我们继续往下探索,就到了另一个parseObject重载:
// com.alibaba.fastjson.JSON
public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
int featureValues, Feature... features) {
if (input == null) {
return null;
}
if (features != null) {
for (Feature feature : features) {
featureValues |= feature.mask;
}
}
DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
if (processor != null) {
if (processor instanceof ExtraTypeProvider) {
parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);
}
if (processor instanceof ExtraProcessor) {
parser.getExtraProcessors().add((ExtraProcessor) processor);
}
if (processor instanceof FieldTypeResolver) {
parser.setFieldTypeResolver((FieldTypeResolver) processor);
}
}
T value = (T) parser.parseObject(clazz, null);
parser.handleResovleTask(value);
parser.close();
return (T) value;
}
可见这个方法返回了最终的反序列化的结果。那么,我们就要死抠它的每一行代码了。敏感的嗅觉提醒我们注意其中所调用的 com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.lang.reflect.Type, java.lang.Object)
。我们看看这个方法的源码。
// com.alibaba.fastjson.parser.DefaultJSONParser
public <T> T parseObject(Type type, Object fieldName) {
...
ObjectDeserializer deserializer = config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
...
return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
} else {
return (T) deserializer.deserialze(this, type, fieldName);
}
} catch (JSONException e) {
throw e;
} catch (Throwable e) {
throw new JSONException(e.getMessage(), e);
}
}
看到了吧,这里调用了上面提到的ParserConfig#getDeserializer
。程序就是根据它返回的 ObjectDeserializer实例,进行deserialze。
依然暂且把ParserConfig#getDeserializer
当做一个黑盒。我们先看看debug出来的都是什么ObjectDeserializer。
CASE1: Java类 有 默认无参构造器
先上图。
从名字FastjsonASMDeserializer_1_User@1352
可知,此时的ObjectDeserializer是一个“临时”反序列化器————阿里Fastjson库利用字节码技术动态生成的Java类反序列化器。
CASE2: Java类 没有 默认无参构造器
此时的ObjectDeserializer实例是一个JavaBeanDeserializer。
至此,一切谜团就都在ParserConfig#getDeserializer(java.lang.reflect.Type)
上了。
§2. ParserConfig#getDeserializer
源码分析
ParserConfig#getDeserializer(java.lang.reflect.Type)
方法里重点是调用了这个类的createJavaBeanDeserializer
方法。该方法作用正是创建所需的反序列化器。
这个方法里有一个局部变量asmEnable,它首先使用到当前对象的asmEnable标志。asmEnable表示是否开启ASM,默认在java环境中自动开启ASM。然后,方法里会针对各种分支逻辑,来更改局部变量asmEnable的值,决定如何生成反序列化器实例。
CASE1: Java类 有 默认无参构造器
先说ASM是什么东东?
ASM: a very small and fast Java bytecode manipulation framework.
ASM 是一个 Java 字节码操作框架,全称为 "ASM"(全称为:Abstract Syntax Model)。ASM 框架由法国电信(France Telecom)的研究员 Eric Bruneton 及其团队开发,而后被移交给 OW2 组织进行维护。ASM 允许开发者直接操作 Java 字节码,可以用于生成、转换和分析 Java 类文件。它在许多开源项目和框架中被使用,如 Spring Framework、Hibernate、JUnit 等。同样,阿里巴巴Fastjson也使用了ASM。ASM 的优势在于其轻量级和高性能。相比于其他 Java 字节码操作框架,ASM 更加灵活,并且由于其直接操作字节码的特性,通常比反射等方式更加高效。
在当前”Java类 有 默认无参构造器“CASE下,我们来分析createJavaBeanDeserializer
方法代码。可以看到,程序经过对目标class、目标class的fields的一顿判断后,变量asmEnable依然是true。最后调用 JavaBeanInfo.build(clazz, type, propertyNamingStrategy)
,并基于构建的beanInfo
对象,利用字节码技术动态生成ObjectDeserializer实例。
利用字节码技术动态生成ObjectDeserializer实例,参见com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory#createJavaBeanDeserializer
。阅读这段源码可以明白上面debug代码中的FastjsonASMDeserializer_1_User@1352
的来源。
// com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory
public ObjectDeserializer createJavaBeanDeserializer(ParserConfig config, JavaBeanInfo beanInfo) throws Exception {
Class<?> clazz = beanInfo.clazz;
if (clazz.isPrimitive()) {
throw new IllegalArgumentException("not support type :" + clazz.getName());
}
String className = "FastjsonASMDeserializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
String classNameType;
String classNameFull;
Package pkg = ASMDeserializerFactory.class.getPackage();
if (pkg != null) {
String packageName = pkg.getName();
classNameType = packageName.replace('.', '/') + "/" + className;
classNameFull = packageName + "." + className;
} else {
classNameType = className;
classNameFull = className;
}
ClassWriter cw = new ClassWriter();
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classNameType, type(JavaBeanDeserializer.class), null);
_init(cw, new Context(classNameType, config, beanInfo, 3));
_createInstance(cw, new Context(classNameType, config, beanInfo, 3));
_deserialze(cw, new Context(classNameType, config, beanInfo, 5));
_deserialzeArrayMapping(cw, new Context(classNameType, config, beanInfo, 4));
byte[] code = cw.toByteArray();
Class<?> deserClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
Constructor<?> constructor = deserClass.getConstructor(ParserConfig.class, JavaBeanInfo.class);
Object instance = constructor.newInstance(config, beanInfo);
return (ObjectDeserializer) instance;
}
debug程序时Debugger里Frames Tab 和 Thread Dump 截图如下
CASE2: Java类 没有 默认无参构造器
同样,我们来分析createJavaBeanDeserializer
方法代码。可以看到,在 JavaBean 没有默认构造器时,程序就不再使用ASM了(变量asmEnable值是false),而是直接实例化一个JavaBeanDeserializer。
下面是createJavaBeanDeserializer
源码,结合程序debug,我添加了注释文字,辅助理解。
// com.alibaba.fastjson.parser.ParserConfig
public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type) {
boolean asmEnable = this.asmEnable & !this.fieldBased; //注释:关于this.asmEnable:java环境下为true(为了区别于Android环境)
if (asmEnable) {
JSONType jsonType = TypeUtils.getAnnotation(clazz,JSONType.class);
...
}
if (clazz.getTypeParameters().length != 0) {
asmEnable = false;
}
if (asmEnable && asmFactory != null && asmFactory.classLoader.isExternalClass(clazz)) {
asmEnable = false;
}
if (asmEnable) {
asmEnable = ASMUtils.checkName(clazz.getSimpleName());
}
if (asmEnable) {
...
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz
, type
, propertyNamingStrategy
,false
, TypeUtils.compatibleWithJavaBean
, jacksonCompatible
);
...
Constructor<?> defaultConstructor = beanInfo.defaultConstructor; //注释:因JavaBean没有无参构造器,这里的 defaultConstructor=null
if (asmEnable && defaultConstructor == null && !clazz.isInterface()) { //注释: 走到这里时,asmEnable 依然是 true
asmEnable = false;
}
for (FieldInfo fieldInfo : beanInfo.fields) {
...
} //注释:敲黑板!for循环结束后,变量 asmEnable 的值变成了 false
}
...
if (!asmEnable) { //注释:JavaBean没有无参构造器时,走这儿,直接实例化JavaBeanDeserializer
return new JavaBeanDeserializer(this, clazz, type);
}
//注释:下面逻辑是 JavaBean有默认构造器 时,利用字节码技术框架(ASM)快速生成反序列化器
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);
try {
return asmFactory.createJavaBeanDeserializer(this, beanInfo);
} catch (NoSuchMethodException ex) {
return new JavaBeanDeserializer(this, clazz, type);
} catch (JSONException asmError) {
return new JavaBeanDeserializer(this, beanInfo);
} catch (Exception e) {
throw new JSONException("create asm deserializer error, " + clazz.getName(), e);
}
}
*JavaBeanDeserializer源码分析
JavaBeanDeserializer 这个组件就是用来解析 JSON 数据,并将其映射到 Java Bean 对象的相应属性上,完成数据的转换和处理操作。???
com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer 实现 com.alibaba.fastjson.parser.deserializer.ObjectDeserializer 接口。 它主要为 JavaBean的每个field定义了FieldDeserializer,同时,它实现了deserialze方法的具体逻辑。
上面代码中调用的构造器 JavaBeanDeserializer(this, clazz, type)
里,初始化了一个类型为JavaBeanInfo名为beanInfo的field。debug有参构造器的User时,它长这样:
然后,我们看看 JavaBeanDeserializer#deserialze 方法的关键代码。结合程序debug有参构造器的User,我添加了注释文字,辅助理解。其实主要是获取构造器参数值,然后利用java.lang.reflect反射机制通过构造器创建JavaBean对象。
// com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer
protected <T> T deserialze(DefaultJSONParser parser, //
Type type, //
Object fieldName, //
Object object, // 入参 object 是null
int features, //
int[] setFlags) {
if (type == JSON.class || type == JSONObject.class) { // 注释:原数据是JSON/JSONObject,直接parse
return (T) parser.parse();
}
//...
//...
if (object == null) {
String[] paramNames = beanInfo.creatorConstructorParameters; // 注释:null
final Object[] params; // 注释:构造器的参数值(多个)
if (paramNames != null) {
//...
} else {
FieldInfo[] fieldInfoList = beanInfo.fields;
int size = fieldInfoList.length;
params = new Object[size];
for (int i = 0; i < size; ++i) {
FieldInfo fieldInfo = fieldInfoList[i];
Object param = fieldValues.get(fieldInfo.name);
if (param == null) {//注释:为null时,获取基本类型的默认值
Type fieldType = fieldInfo.fieldType;
param = //...
}
params[i] = param;
}
}
if (beanInfo.creatorConstructor != null) {
//...
// 注释:利用反射创建对象(rt.jar里java.lang.reflect.Constructor#newInstance)
object = beanInfo.creatorConstructor.newInstance(params);
//...
}
}
Method buildMethod = beanInfo.buildMethod;
if (buildMethod == null) {
return (T) object; // 返回结果
}
//...
}
上面获取基本类型的默认值,比较简单,见下方。
if (fieldType == byte.class) param = (byte) 0;
else if (fieldType == short.class) param = (short) 0;
else if (fieldType == int.class) param = 0;
else if (fieldType == long.class) param = 0L;
else if (fieldType == float.class) param = 0F;
else if (fieldType == double.class) param = 0D;
else if (fieldType == boolean.class) param = Boolean.FALSE;
else if (fieldType == String.class && (...) != 0) param = "";
§3. 至此,本文告一段落。
不过,通过测试,发现一个问题,就是 当上面的User的有参构造器不是全参构造器时,反序列化只会把构造器参数包含的field赋值,其余field则是null。
即,下方代码的返回值是 FastJsonTest.User(id=123, name=null)
public void testFastjsonCoDec() {
User object = new User(123L);
object.setName("Eric");
String jsonString = JSON.toJSONString(object);
System.out.println(JSON.parseObject(jsonString, User.class));
}
@Data
public static class User {
private Long id;
private String name;
public User(Long id) {
this.id = id;
}
}
个中缘由,择日再分析。
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/18423766