buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

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.globalcom.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 字节码操作框架,全称为 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;  // 注释:jstudy.jacksoncodec.FastJsonTest$User
    if (clazz.isPrimitive()) {
        throw new IllegalArgumentException("not support type :" + clazz.getName());
    }

    String className = "FastjsonASMDeserializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName(); // 注释:类名 例如:FastjsonASMDeserializer_1_User
    String classNameType;
    String classNameFull;

    Package pkg = ASMDeserializerFactory.class.getPackage(); // 注释:package com.alibaba.fastjson.parser.deserializer
    if (pkg != null) {
        String packageName = pkg.getName(); // 注释:com.alibaba.fastjson.parser.deserializer
        classNameType = packageName.replace('.', '/') + "/" + className; // 注释:com/alibaba/fastjson/parser/deserializer/FastjsonASMDeserializer_1_User
        classNameFull = packageName + "." + className;  // 注释:com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_User
    } else {
        classNameType = className;
        classNameFull = className;
    }

    ClassWriter cw = new ClassWriter();
    cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classNameType, type(JavaBeanDeserializer.class), null); // 注释:从type(JavaBeanDeserializer.class)可知,动态创建的类继承自JavaBeanDeserializer

    _init(cw, new Context(classNameType, config, beanInfo, 3)); // 注释:创建<init>方法
    _createInstance(cw, new Context(classNameType, config, beanInfo, 3)); // 注释:创建createInstance方法
    _deserialze(cw, new Context(classNameType, config, beanInfo, 5)); // 注释:创建具有4个参数的deserialze方法:deserialze(DefaultJSONParser parser, Type type, Object fieldName, int features)。反序列化就靠它了! 其中的type就是我们传入的JavaBean,即FastJsonTest$User
    _deserialzeArrayMapping(cw, new Context(classNameType, config, beanInfo, 4)); // 注释:创建deserialzeArrayMapping方法

    byte[] code = cw.toByteArray(); // 注释:目标类FastjsonASMDeserializer_1_User的字节码
    Class<?> deserClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length); // 注释:通过classLoader在内存中创建FastjsonASMDeserializer_1_User.class
    Constructor<?> constructor = deserClass.getConstructor(ParserConfig.class, JavaBeanInfo.class);
    Object instance = constructor.newInstance(config, beanInfo); // 注释:通过反射机制在内存中创建FastjsonASMDeserializer_1_User实例对象(注意不是JavaBean的对象哦~)

    return (ObjectDeserializer) instance;
}

debug程序时Debugger里Frames Tab 和 Thread Dump 截图如下




我们在上面ASM代码中加断点,将 类字节码 (代码中的code变量)写入到一个.class文件里。这样我们就得到了反序列化所实际执行的代码。分析.class代码,以 deserialze(DefaultJSONParser parser, Type type, Object fieldName, int features) 为例,它是直接new的User对象,然后调用setter。自然是比反射快!

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.alibaba.fastjson.parser.deserializer;

import ...

public class FastjsonASMDeserializer_1_User extends JavaBeanDeserializer {
    ...
    public FastjsonASMDeserializer_1_User(ParserConfig var1, JavaBeanInfo var2) {
        super(var1, var2);
    }

    public Object createInstance(DefaultJSONParser var1, Type var2) {
        return new FastJsonTest.User();
    }

    public Object deserialze(DefaultJSONParser parser, Type type, Object fieldName, int features) {
        ...
            FastJsonTest.User user;
            ...
				user = new FastJsonTest.User();
				...

                    if ((var12 & 1) != 0) {
                        user.setId(var14);
                    }
                    if ((var12 & 2) != 0) {
                        user.setName(var15);
                    }
                }
                ...
                return user;
            }

            if ((var12 & 1) != 0) {
                user.setId(var14);
            }
            if ((var12 & 2) != 0) {
                user.setName(var15);
            }

            return (FastJsonTest.User)this.parseRest(parser, type, fieldName, user, features, new int[]{var12});
        ...
    }



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。 **这个if条件满足
            asmEnable = false; //注释:asmEnable 的值由 true 被改为 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;
        }
    }

个中缘由,择日再分析。

posted on 2024-09-21 11:33  buguge  阅读(99)  评论(0编辑  收藏  举报