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; } }
个中缘由,择日再分析。