fastjson源码--进大厂的最后一脚 下

477 阅读23分钟

2.3.8.2 反序列化过程

反序列化的含义是将跨语言的json字符串转换成java对象

思路

public static T parseObject(String text, Class clazz) { /** 根据指定text,返回期望的java对象类型class */ return parseObject(text, clazz, new Feature[0]); }

关键步骤 1创建解析配置ParserConfig对象,包括初始化内部反序列化实例和特性配置等。

2添加反序列化拦截器

3根据具体类型查找反序列化实例,执行反序列化转换

4解析对象内部引用

基本概念 词法分析是反序列化的重要基础,在其他框架druid、parsii等框架都用到了词法分析的技术,个人认为在讲反序列化之前讲词法分析更重要。

  • token-词法标记 用于标识当前在解析过程中解析到的对象的一个标记,具体的值参考 JSONToken。比如 {,即表示当前正在解析的是一个类似map或对象的格式,而},则表示当前对象已经到底了。
  • ch-当前字符 用于表示当前已经读取到的字符是什么,如 abc,当位置为1时,则当前字符为 b
  • bp-解析字符位置 用于表示当前字符所位于原始字符串中的哪一个位置,与ch是相对应的,它始终表示最新的一个位置,如果需要记录一些历史位置。如字符串起始位置,数字起始位置等,则需要使用其它标记,如np。
  • sbuf-字符缓冲 在解析字符串时的特殊存储区域,主要是用于解析转义字符时的临时存储区。即如果原字符串为 a\t,则实际解析的字符串应该为a\t,那么原字符串为3位长,解析之后为2位长。即需要另行存储。字符缓冲区如名所示,为一个字符数组,需要需要单独的定义来存储长度信息,如使用sp。
  • sp-字符缓冲区位置 这个用于表示在字符缓冲区之间记录当前字符串(或数字串)等的长度信息,同时也等同于当前的一个位置(如果坐标从0开始)。
  • np-数字解析位置 用于实际表示在解析到常量信息时起始点的标记位置。通过np + sp,即计算得出相应的区间值了。

解析规则 定义规则

  • 字符串 以"开头,并且以"结束,在中间可以存在以\为转义符,后面接"的情况,如 """,认为是正确的。但 """",认为是错误的,其解析时在倒数第2个双引号时即结尾了。
  • 数字整形 以0-9开始,并且以连续数字,末尾可以s,b,f,d,l等
  • 数字小数 以0-9开始,默认为f,D或者,中间存在小数点
  • bool值 必须是 true 或 false ,全小写
  • 数组集合 以[ 开头,以]结尾,中间以任意,分隔的信息
  • set 以Set开头的,后面接数组集合的信息
  • treeSet 以TreeSet开头的,后面接数组集合的信息
  • 对象map 以{开始,以}结尾,中间按key,value集合的信息.
  • null值 必须为null
  • 字段 字符串,或单引号字符串,或无引号字符串,无引号时,必须为a-zA-Z_开头,以a-zA-Z0-9_结束.

词法规则 按照上面的定义,实际上就是贪婪的匹配规则,一旦满足一个匹配规则,那么这个匹配就要继续下去,直到当前规则不能完成时,同时在下一个规则之间,使用特定的分隔符作连接。 如 字符串 {a:"123",b:[1,2,3]},即按以下规则进行 1.对象开始:{ 2.对象key(即字段):a 3.分隔符: : 4.对象value开始: 5.字符串开始: " 6.字符串:123 7.字符串结束: " 8.对象value:结束: 9.对象间分隔符:, 10.对象key: b 11.分隔符: : 12.对象value开始: 13.数组开始: [ 14.数组值1数字开始: 1 15.数组值1数字结束: 1, 16.数组分隔符: , 17.… 18.数组结束: ] 19.对象结束: }

词法解析 整个词法,即TOKEN流,是由类JSONLexerBase来负责完成的,其负责提供主要词法单元的解析和结果取值操作。相应方法对应关系如下所示

  • 数字 scanNumber numberString intValue longValue floatValue doubleValue
  • 字符串 scanString stringVal
  • NULL值 scanNULL,scanNullOrNew
  • Boolean值 scanTrue,scanFalse

语法解析 1结束符判定 在词法解析上,fastjson根据第1个有效字符判定相应的类型,然后直到该类型结束之后,立即采用该类型。如以{开头,则一定返回object类型。对于类型 "{key:value} other" 这种非正确字符串,fastjson采用尾判断规则,即有效对象解析完毕之后,判定结束符必须已经到达字符串尾。如果Token值不是EOF,则表示字符串出现问题,而提示相错误的信息。 具体的判定,对应方法 DefaultJSONParser中的close()方法。

2忽略类型 默认情况下,fastjson对于json类型分别采用 set,treeSet,jsonArray,jsonObject,int(包含int,long,biginteger),float(包含float,double,bigDecimal),string,null,true,false来进行解析。即除基本的value词法表示外,其它均使用通用类型来表示。即数组使用set和jsonArray,对象使用jsonObject。

3数组解析 对应方法 Object parse(Object fieldName)

case LBRACKET: JSONArray array = new JSONArray(); parseArray(array, fieldName); return array;

如上所示,语法规则 [ 开始,表示为数组,则定义jsonArray,然后将此引用传递给具体的解析数组的方法中,以进行处理。jsonArray可以理解为使用ArrayList封装的复合对象。上面的fieldName解析对象时标识相应的key值,这里默认为null。以下代码忽略非关键性处理.

public final void parseArray(final Collection array, Object fieldName) {
.....
    final JSONLexer lexer = getLexer();
    //因为当前位置为[ 跳转到下一个词法单元处
    lexer.nextToken(JSONToken.LITERAL_STRING);
 
        for (int i = 0;; ++i) {
......
            Object value;
            switch (lexer.token()) {
                case LITERAL_INT:
//当前值为int,解析value值,并跳转到下一个标记处
                    value = lexer.integerValue();
//如当前字符串为 1,2 则解析完1之后,跳转到,处,以方便后面作判断,并跳转至2处
//这里的nextToken中的参数表示期望值,但实际上也不一定是该值,不过可以根据该值作一个进一步判定相应值。如这里期望,但也可以是一个 ],而表示解析结束
                    lexer.nextToken(JSONToken.COMMA);
                    break;
                case LITERAL_FLOAT:
......//解析小数
                case LITERAL_STRING:
......//解析字符串
                    break;
                case TRUE://解析true
                case FALSE://解析false
                case LBRACE:
//这里碰到一个{,表示数组中还内嵌有对象,则跳转到解析对象的地方
                    JSONObject object = new JSONObject(isEnabled(Feature.OrderedField));
                    value = parseObject(object, i);
                    break;
                case LBRACKET:
//这里碰到 [,则表示数组中还内嵌有数组,跳转到解析子数组的地方,这里的items值是新数组
                    Collection items = new JSONArray();
                    parseArray(items, i);
                    value = items;
                    break;
                case NULL://解析null值
                case UNDEFINED://js中的undefiend也认为是null值
                case RBRACKET:
//这里碰到 ],则表示当前数组已经解析完毕,可以正常的return了。其它地方都是继续循环处理,即只有在这里才能正常跳出循环
                    lexer.nextToken(JSONToken.COMMA);
                    return;
                case EOF:
//非正常跳出循环的地方,即字符串一下未匹配到] 就到末尾了
                    throw new JSONException("unclosed jsonArray");
                default:
//默认解析
                    value = parse();
                    break;
            }
 
//将上面解析到的对象添加到数组中
            array.add(value);
 
//这里因为前面在解析完之后,均往前解析了一位,即期望碰到 ,这里判断如果是 ,值 表示还有后续的value值
//则继续往前解析,如 a,b 则期望当前位置从,处解析到b处
            if (lexer.token() == JSONToken.COMMA) {
                lexer.nextToken(JSONToken.LITERAL_STRING);
                continue;
            }
        }

类型处理 上面的为不带类型处理,则fastjson不知道应该将返回类型设置为什么类型。如果调用方主动的提示处理,则采用另一种处理方式,则根据提示类型对array以及object作处理。实际上,带类型处理的话,则会使用到ObjectDeserializer来进行解析。不过这里又会重新调用 DefaultJSONParser来进行处理。如对于(数组)或集合,则会使用 parseArray(Type type, Collection array, Object fieldName) 通过传递回来的collection对象,将相应值并根据type进行解析,再放回集合中。并根据返回类型进行调整。如数组,则会使用jsonArray进行二次转换来得到最终结果。代码参考如下:

public void parseArray(Type type, Collection array, Object fieldName) {
//这里根据type值获取具体类型的解析器
        ObjectDeserializer deserializer = null;
        if (int.class == type) {
            deserializer = IntegerCodec.instance;
            lexer.nextToken(JSONToken.LITERAL_INT);
        }......
 
            for (int i = 0;; ++i) {
......
                if (int.class == type) {
                    Object val = IntegerCodec.instance.deserialze(this, null, null);
                    array.add(val);
                }
......
 
//下一个元素解析
                if (lexer.token() == JSONToken.COMMA) {
                    lexer.nextToken(deserializer.getFastMatchToken());
                    continue;
                }
            }

可以看出,此处解析与通用解析逻辑基本一致,惟一不同的即是这里使用了针对类型的各种deserializer解析器来完成针对类型的工作。

面向对象封装 在实际的使用场景,我们均会使用到如 parseObject(String text, Class clazz) 来期望返回具体的类型,这里实际上就会调用到了不同的类型反序列化器了。fastjson根据这里的类型,调用相应的序列化对象来完成不同的对象解析工作。

1类型映射 而序列化器的工作也并不是进行具体的语法解析,而是提供相应的类型信息,以期望jsonParser进行正常的解析工作。即具体的解析工作仍是由jsonParser来完成,ObjectDeserializer只不过提供一些上下文信息,以及对流程进行控制。

从 ObjectDeserializer的继承上可以看出,存在很多不同的反序列化器。同时,对于fastjson内置的反序列化器,采用了 ParserConfig.derializers来进行内部存储。因为默认情况下,会使用parseConfig的单例对象,因此这里的存储是全局共享的(实际上也没有关系)

2数字解析 对应类为IntegerCodec

public <T> T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) {
        final JSONLexer lexer = parser.getLexer();
 
        Integer intObj;
        if (lexer.token() == JSONToken.LITERAL_INT) {
//如果token匹配到数字,而直接通过integer.parse强转
            int val = lexer.intValue();
            lexer.nextToken(JSONToken.COMMA);
            intObj = Integer.valueOf(val);
        } else if (lexer.token() == JSONToken.LITERAL_FLOAT) {
//匹配到小数,而这里需要整数,而截取掉
            BigDecimal decimalValue = lexer.decimalValue();
            lexer.nextToken(JSONToken.COMMA);
            intObj = Integer.valueOf(decimalValue.intValue());
        } else {
//其它类型,采用类型转换强制转换,如匹配到字符串,也是可以转换为 整数的
            Object value = parser.parse();
 
            intObj = TypeUtils.castToInt(value);
        }
         
//单独处理,使用IntegerCodec同时支持 int和 atomicInteger两种,算是偷懒吧
        if (clazz == AtomicInteger.class) {
            return (T) new AtomicInteger(intObj.intValue());
        }
         
        return (T) intObj;
    }

3数组(集合)解析 对应类CollectionDeserializer 数组对应为ArrayDeserializer

public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
 
//根据类型获取不同的集合实现
        Class<?> rawClass = getRawClass(type);
 
//以下即根据不同的接口类 作不同的实现。因此在应用中,经常使用接口来标识不同的集合类
        Collection list;
        if (rawClass == AbstractCollection.class) {//ArrayList
        } else if (rawClass.isAssignableFrom(HashSet.class)) {//HashSet
        } else if (rawClass.isAssignableFrom(LinkedHashSet.class)) {//LinkedHashSet
        } else if (rawClass.isAssignableFrom(TreeSet.class)) {//TreeSet
        } else if (rawClass.isAssignableFrom(ArrayList.class)) {//List
        } else if (rawClass.isAssignableFrom(EnumSet.class)) {//EnumSet
        } else {//默认情况下,直接实例化
                list = (Collection) rawClass.newInstance();
        }
 
//这里尝试获取泛型实例信息,如 List<String> 则获取string,即期望集合中每一项值均是 字符串
        Type itemType;
        if (type instanceof ParameterizedType) {
            itemType = ((ParameterizedType) type).getActualTypeArguments()[0];
        } else {
            itemType = Object.class;
        }
//调用3.3中的不同类型解析公式处理
        parser.parseArray(itemType, list, fieldName);
 
        return (T) list;
    }

4 javaBean封装解析

在实际解析过程中 javaBean与map的解析规则基本上一致。不过在map中的key值是任意的,而在javaBean中key值是固定的。即javaBean中可以控制在反序列化时哪些key是可接收的,哪些是不可接收的。

同时,javaBean通过配置项JsonField,可以重新配置字段的别名,从而映射到其它的key上。在map中, 均不存在相应的处理.

javaBean的过程可以理解为,先创建对象,然后每于{}中的每一项,先匹配key值,然后根据key值查找到相应的字段信息,根据不同的字段再解析该字段值。即语法表中的 pair: STRING ':' value ;

具体的解析代码如下所示(忽略非关键信息)

public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, Object object) {
//简化判断,即针对原生json对象,使用类似map的解析规则
        if (type == JSON.class || type == JSONObject.class) {
            return (T) parser.parse();
        }
         
//如果直接是一个null,则直接返回null即可,表示该对象不存在
        if (lexer.token() == JSONToken.NULL) {
            lexer.nextToken(JSONToken.COMMA);
            return null;
        }
 
            Map<String, Object> fieldValues = null;
 
//预处理,如果直接为 {},则表示是空对象(不是null,直接返回
            if (lexer.token() == JSONToken.RBRACE) {
                lexer.nextToken(JSONToken.COMMA);
                    object = createInstance(parser, type);
                return (T) object;
            }
......
 
            for (;;) {
//查找字段值
                String key = lexer.scanSymbol(parser.getSymbolTable());
......
//这里因为找到了具体的字段,则根据字段进行解析该字段信息
                boolean match = parseField(parser, key, object, type, fieldValues);
//这里碰到了},表示对象已经解析结束,就不再处理了                
                if (lexer.token() == JSONToken.RBRACE) {
                    lexer.nextToken(JSONToken.COMMA);
                    break;
                }
            }
 
            return (T) object;

源码

public static T parseObject(String text, Class clazz) { /** 根据指定text,返回期望的java对象类型class */ return parseObject(text, clazz, new Feature[0]); }

这个反序列化接口可以处理对象包含任意字段类型,但是自身不能是泛型类型,原因是java的运行时类型擦除。fastjson给出了替代方法解决:

String json = "[{},...]"; Type listType = new TypeReference<List>() {}.getType(); List modelList = JSON.parseObject(json, listType);

继续分析内部调用parseObject public static T parseObject(String json, Class clazz, Feature... features) { return (T) parseObject(json, (Type) clazz, ParserConfig.global, null, DEFAULT_PARSER_FEATURE, features); }

//反序列化的过程

 public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
                                          int featureValues, Feature... features) {
        if (input == null || input.isEmpty()) {
            return null;
        }
        /** 配置反序列化时启用的特性,比如是否允许json字符串字段不包含双引号 */
        if (features != null) {
            for (Feature feature : features) {
                featureValues |= feature.mask;
            }
        }
        /**
        *  初始化DefaultJSONParser,反序列化类型由它
        *  委托config查找具体序列化处理器处理
        */
        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);
            }
        }
        /** 使用反序列化实例转换对象,查找具体序列化实例委托给config查找 */
        T value = (T) parser.parseObject(clazz, null);
        /** 处理json内部引用协议格式对象 */
        parser.handleResovleTask(value);

        parser.close();

        return (T) value;
    }

继续查看parser.parseObject(clazz, null)逻辑:

public <T> T parseObject(Type type, Object fieldName) {
        int token = lexer.token();
        /** 获取json串第一个有效token */
        if (token == JSONToken.NULL) {
            /** 如果返回时null,自动预读下一个token */
            lexer.nextToken();
            return null;
        }
        /** 判定token属于字符串 */
        if (token == JSONToken.LITERAL_STRING) {
            if (type == byte[].class) {
                /** 获取byte字节数据,分为十六进制和base64编码 */
                byte[] bytes = lexer.bytesValue();
                lexer.nextToken();
                return (T) bytes;
            }
            /** 获取字符数组, 特殊处理String内存占用 */
            if (type == char[].class) {
                String strVal = lexer.stringVal();
                lexer.nextToken();
                return (T) strVal.toCharArray();
            }
        }

        ObjectDeserializer deserializer = config.getDeserializer(type);
        /** 委托config进行特定类型查找反序列化实例 */
        try {
            if (deserializer.getClass() == JavaBeanDeserializer.class) {
                if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
                throw new JSONException("syntax error,except start with { or [,but actually start with "+ lexer.tokenName());
            }
                /** 执行反序列化 */
                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(java.lang.reflect.Type)自己查看逻辑:

//查找反序列化实例
    public ObjectDeserializer getDeserializer(Type type) {
        /** 首先从内部已经注册查找特定class的反序列化实例 */
        ObjectDeserializer deserializer = get(type);
        if (deserializer != null) {
            /** 引用类型,根据特定类型再次匹配 */
            return deserializer;
        }

        if (type instanceof Class<?>) {
            return getDeserializer((Class<?>) type, type);
        }

        if (type instanceof ParameterizedType) {
            /** 获取泛型类型原始类型 */
            Type rawType = ((ParameterizedType) type).getRawType();
            /** 泛型原始类型是引用类型,根据特定类型再次匹配 */
            if (rawType instanceof Class<?>) {
                return getDeserializer((Class<?>) rawType, type);
            } else {
                /** 递归调用反序列化查找 */
                return getDeserializer(rawType);
            }
        }

        if (type instanceof WildcardType) {
            /** 类型是通配符或者限定类型 */
            WildcardType wildcardType = (WildcardType) type;
            Type[] upperBounds = wildcardType.getUpperBounds();
            if (upperBounds.length == 1) {
                Type upperBoundType = upperBounds[0];
                /** 获取泛型上界(? extends T),根据特定类型再次匹配 */
                return getDeserializer(upperBoundType);
            }
        }
        /** 如果无法匹配到,使用默认JavaObjectDeserializer反序列化 */
        return JavaObjectDeserializer.instance;
    }

反序列化匹配getDeserializer(Type)主要特定处理了泛型类型,取出泛型类型真实类型还是委托内部ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type)进行精确类型查找:

public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
        /** 首先从内部已经注册查找特定type的反序列化实例 */
        ObjectDeserializer deserializer = get(type);
        if (deserializer != null) {
            return deserializer;
        }

        if (type == null) {
            type = clazz;
        }
        /** 再次从内部已经注册查找特定class的反序列化实例 */
        deserializer = get(type);
        if (deserializer != null) {
            return deserializer;
        }

        {
            JSONType annotation = TypeUtils.getAnnotation(clazz,JSONType.class);
            if (annotation != null) {
                Class<?> mappingTo = annotation.mappingTo();
                /** 根据类型注解指定的反序列化类型 */
                if (mappingTo != Void.class) {
                    return getDeserializer(mappingTo, mappingTo);
                }
            }
        }

        if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
            /** 根据泛型真实类型查找反序列化实例 */
            deserializer = get(clazz);
        }

        if (deserializer != null) {
            return deserializer;
        }

        for (Module module : modules) {
            deserializer = module.createDeserializer(this, clazz);
            if (deserializer != null) {
                putDeserializer(type, deserializer);
                return deserializer;
            }
        }
        /** 获取class名称,进行类型匹配(可以支持高版本jdk和三方库) */
        String className = clazz.getName();
        className = className.replace('$', '.');

        if (className.startsWith("java.awt.") //
            && AwtCodec.support(clazz)) {
            /**
             *  如果class的name是"java.awt."开头 并且
             *  继承 Point、Rectangle、Font或者Color 其中之一
             */
            if (!awtError) {
                String[] names = new String[] {
                        "java.awt.Point",
                        "java.awt.Font",
                        "java.awt.Rectangle",
                        "java.awt.Color"
                };

                try {
                    for (String name : names) {
                        if (name.equals(className)) {
                            /** 如果系统支持4中类型, 使用AwtCodec 反序列化 */
                            putDeserializer(Class.forName(name), deserializer = AwtCodec.instance);
                            return deserializer;
                        }
                    }
                } catch (Throwable e) {
                    // skip
                    awtError = true;
                }

                deserializer = AwtCodec.instance;
            }
        }

        if (!jdk8Error) {
            try {
                if (className.startsWith("java.time.")) {
                    String[] names = new String[] {
                            "java.time.LocalDateTime",
                            "java.time.LocalDate",
                            "java.time.LocalTime",
                            "java.time.ZonedDateTime",
                            "java.time.OffsetDateTime",
                            "java.time.OffsetTime",
                            "java.time.ZoneOffset",
                            "java.time.ZoneRegion",
                            "java.time.ZoneId",
                            "java.time.Period",
                            "java.time.Duration",
                            "java.time.Instant"
                    };

                    for (String name : names) {
                        if (name.equals(className)) {
                            /** 如果系统支持JDK8中日期类型, 使用Jdk8DateCodec 反序列化 */
                            putDeserializer(Class.forName(name), deserializer = Jdk8DateCodec.instance);
                            return deserializer;
                        }
                    }
                } else if (className.startsWith("java.util.Optional")) {
                    String[] names = new String[] {
                            "java.util.Optional",
                            "java.util.OptionalDouble",
                            "java.util.OptionalInt",
                            "java.util.OptionalLong"
                    };
                    for (String name : names) {
                        if (name.equals(className)) {
                            /** 如果系统支持JDK8中可选类型, 使用OptionalCodec 反序列化 */
                            putDeserializer(Class.forName(name), deserializer = OptionalCodec.instance);
                            return deserializer;
                        }
                    }
                }
            } catch (Throwable e) {
                // skip
                jdk8Error = true;
            }
        }

        if (!jodaError) {
            try {
                if (className.startsWith("org.joda.time.")) {
                    String[] names = new String[] {
                            "org.joda.time.DateTime",
                            "org.joda.time.LocalDate",
                            "org.joda.time.LocalDateTime",
                            "org.joda.time.LocalTime",
                            "org.joda.time.Instant",
                            "org.joda.time.Period",
                            "org.joda.time.Duration",
                            "org.joda.time.DateTimeZone",
                            "org.joda.time.format.DateTimeFormatter"
                    };

                    for (String name : names) {
                        if (name.equals(className)) {
                            putDeserializer(Class.forName(name), deserializer = JodaCodec.instance);
                            return deserializer;
                        }
                    }
                }
            } catch (Throwable e) {
                // skip
                jodaError = true;
            }
        }

        if ((!guavaError) //
                && className.startsWith("com.google.common.collect.")) {
            try {
                String[] names = new String[] {
                        "com.google.common.collect.HashMultimap",
                        "com.google.common.collect.LinkedListMultimap",
                        "com.google.common.collect.LinkedHashMultimap",
                        "com.google.common.collect.ArrayListMultimap",
                        "com.google.common.collect.TreeMultimap"
                };

                for (String name : names) {
                    if (name.equals(className)) {
                        putDeserializer(Class.forName(name), deserializer = GuavaCodec.instance);
                        return deserializer;
                    }
                }
            } catch (ClassNotFoundException e) {
                // skip
                guavaError = true;
            }
        }

        if (className.equals("java.nio.ByteBuffer")) {
            putDeserializer(clazz, deserializer = ByteBufferCodec.instance);
        }

        if (className.equals("java.nio.file.Path")) {
            putDeserializer(clazz, deserializer = MiscCodec.instance);
        }

        if (clazz == Map.Entry.class) {
            putDeserializer(clazz, deserializer = MiscCodec.instance);
        }

        if (className.equals("org.javamoney.moneta.Money")) {
            putDeserializer(clazz, deserializer = MonetaCodec.instance);
        }

        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            /** 使用当前线程类加载器 查找 META-INF/services/AutowiredObjectDeserializer.class实现类 */
            for (AutowiredObjectDeserializer autowired : ServiceLoader.load(AutowiredObjectDeserializer.class,
                                                                            classLoader)) {
                for (Type forType : autowired.getAutowiredFor()) {
                    putDeserializer(forType, autowired);
                }
            }
        } catch (Exception ex) {
            // skip
        }

        if (deserializer == null) {
            deserializer = get(type);
        }

        if (deserializer != null) {
            return deserializer;
        }

        if (clazz.isEnum()) {
            if (jacksonCompatible) {
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (TypeUtils.isJacksonCreator(method)) {
                        deserializer = createJavaBeanDeserializer(clazz, type);
                        putDeserializer(type, deserializer);
                        return deserializer;
                    }
                }
            }

            Class<?> deserClass = null;
            JSONType jsonType = TypeUtils.getAnnotation(clazz, JSONType.class);
            if (jsonType != null) {
                deserClass = jsonType.deserializer();
                try {
                    /** 如果是枚举类型并使用了注解,使用注解指定的反序列化 */
                    deserializer = (ObjectDeserializer) deserClass.newInstance();
                    putDeserializer(clazz, deserializer);
                    return deserializer;
                } catch (Throwable error) {
                    // skip
                }
            }
            /** 如果是枚举类型,使用EnumSerializer反序列化 */
            deserializer = new EnumDeserializer(clazz);
        } else if (clazz.isArray()) {
            /** 如果是数组类型,使用数组对象反序列化实例 */
            deserializer = ObjectArrayCodec.instance;
        } else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class
                   || clazz == ArrayList.class) {
            /** 如果class实现集合接口,使用CollectionCodec反序列化 */
            deserializer = CollectionCodec.instance;
        } else if (Collection.class.isAssignableFrom(clazz)) {
            deserializer = CollectionCodec.instance;
        } else if (Map.class.isAssignableFrom(clazz)) {
            /** 如果class实现Map接口,使用MapDeserializer反序列化 */
            deserializer = MapDeserializer.instance;
        } else if (Throwable.class.isAssignableFrom(clazz)) {
            /** 如果class继承Throwable类,使用ThrowableDeserializer反序列化 */
            deserializer = new ThrowableDeserializer(this, clazz);
        } else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
            deserializer = new PropertyProcessableDeserializer((Class<PropertyProcessable>) clazz);
        } else if (clazz == InetAddress.class) {
            deserializer = MiscCodec.instance;
        } else {
            /** 默认使用JavaBeanDeserializer反序列化(没有开启asm情况下) */
            deserializer = createJavaBeanDeserializer(clazz, type);
        }
        /** 加入cache,避免同类型反复创建 */
        putDeserializer(type, deserializer);

        return deserializer;
}

总结 其实查找反序列化和之前提到了序列化类似,根据特定类型匹配接口或者继承实现类查找的 其实在整个过程中,最核心的部分在于词法分析和语法解析.另外由于业务的复杂性以及在编码时的代码优化,fastjson在一些具体实现时,有一些额外的处理,导致逻辑上不是很懂。但是,理解了词法和语法,其它的看起来就不是太难了。 其它

  • JsonField 用于描述字段的序列信息及反序列信息,如重新设定name值,是否需要反序列化等.
  • JsonType 用于控制指定的类进行反序列化时的信息,如使用其它类作为映射信息.
  • JsonCreator 用于控制在反序列化时初始化object时,采用哪一个构造函数或者工厂方法
  • ExtraTypeProvider 用于控制在javaBean序列化时,字段的类型信息重新设定。比如 字段类型为Object,可以通过这个接口重新设定它的具体类型.
  • ExtraProcessor 用于控制在序列化及反序列化时,如果碰到不同的解析的字段时,可以通过此接口重新 进行处理。如 字段a,在{"b":"abc"},可以通过这个接口重新将字段b所对应的值映射到字段a上。
  • ObjectDeserializer 最后可以自已定义反序列化器,通过ParseConfig.putDeserializer 来添加相应的反序列化器

具体词法语法分析函数

2.3.9 springboot集成FastJSON

1.引jar

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.47</version>
   </dependency>

2.3.10 Fastjson中的优化

2.3.10.1 Fastjson中Serialzie的优化实现

1、自行编写类似StringBuilder的工具类SerializeWriter。 把java对象序列化成json文本,是不可能使用字符串直接拼接的,因为这样性能很差。比字符串拼接更好的办法是使用java.lang.StringBuilder。StringBuilder虽然速度很好了,但还能够进一步提升性能的,fastjson中提供了一个类似StringBuilder的类com.alibaba.fastjson.serializer.SerializeWriter。

SerializeWriter提供一些针对性的方法减少数组越界检查。例如public void writeIntAndChar(int i, char c) {},这样的方法一次性把两个值写到buf中去,能够减少一次越界检查。目前SerializeWriter还有一些关键的方法能够减少越界检查的,我还没实现。也就是说,如果实现了,能够进一步提升serialize的性能。

2、使用ThreadLocal来缓存buf。 这个办法能够减少对象分配和gc,从而提升性能。SerializeWriter中包含了一个char[] buf,每序列化一次,都要做一次分配,使用ThreadLocal优化,能够提升性能。

3、使用asm避免反射 获取java bean的属性值,需要调用反射,fastjson引入了asm的来避免反射导致的开销。fastjson内置的asm是基于objectweb asm 3.3.1改造的,只保留必要的部分,fastjson asm部分不到1000行代码,引入了asm的同时不导致大小变大太多。

4、使用一个特殊的IdentityHashMap优化性能。 fastjson对每种类型使用一种serializer,于是就存在class -> JavaBeanSerizlier的映射。fastjson使用IdentityHashMap而不是HashMap,避免equals操作。我们知道HashMap的算法的transfer操作,并发时可能导致死循环,但是ConcurrentHashMap比HashMap系列会慢,因为其使用volatile和lock。fastjson自己实现了一个特别的IdentityHashMap,去掉transfer操作的IdentityHashMap,能够在并发时工作,但是不会导致死循环。

5、缺省启用sort field输出 json的object是一种key/value结构,正常的hashmap是无序的,fastjson缺省是排序输出的,这是为deserialize优化做准备。

6、集成jdk实现的一些优化算法 在优化fastjson的过程中,参考了jdk内部实现的算法,比如int to char[]算法等等。

2.3.10.2 Fastjson中deserializer的主要优化实现

deserializer也称为parser或者decoder,fastjson在这方面投入的优化精力最多。 1、读取token基于预测。 所有的parser基本上都需要做词法处理,json也不例外。fastjson词法处理的时候,使用了基于预测的优化算法。比如key之后,最大的可能是冒号":",value之后,可能是有两个,逗号","或者右括号"}"。在com.alibaba.fastjson.parser.JSONScanner中提供了这样的方法: Java代码

从上面摘抄下来的代码看,基于预测能够做更少的处理就能够读取到token。 2、sort field fast match算法 fastjson的serialize是按照key的顺序进行的,于是fastjson做deserializer时候,采用一种优化算法,就是假设key/value的内容是有序的,读取的时候只需要做key的匹配,而不需要把key从输入中读取出来。通过这个优化,使得fastjson在处理json文本的时候,少读取超过50%的token,这个是一个十分关键的优化算法。基于这个算法,使用asm实现,性能提升十分明显,超过300%的性能提升。 { "id" : 123, "name" : "魏加流", "salary" : 56789.79}


在上面例子看,虚线标注的三个部分是key,如果key_id、key_name、key_salary这三个key是顺序的,就可以做优化处理,这三个key不需要被读取出来,只需要比较就可以了。

这种算法分两种模式,一种是快速模式,一种是常规模式。快速模式是假定key是顺序的,能快速处理,如果发现不能够快速处理,则退回常规模式。保证性能的同时,不会影响功能。

在这个例子中,常规模式需要处理13个token,快速模式只需要处理6个token。

实现sort field fast match算法的代码在这个类[com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory|code.alibabatech.com/svn/fastjso… 这里是有一个用于演示sort field fast match算法的代码: code.alibabatech.com/svn/fastjso…

// 用于快速匹配的每个字段的前缀

char[] size_   = "\"size\":".toCharArray();  
char[] uri_    = "\"uri\":".toCharArray();  
char[] titile_ = "\"title\":".toCharArray();  
char[] width_  = "\"width\":".toCharArray();  
char[] height_ = "\"height\":".toCharArray();  
  
// 保存parse开始时的lexer状态信息  
int mark = lexer.getBufferPosition();  
char mark_ch = lexer.getCurrent();  
int mark_token = lexer.token();  
  
int height = lexer.scanFieldInt(height_);  
if (lexer.matchStat == JSONScanner.NOT_MATCH) {  
  // 退出快速模式, 进入常规模式  
   lexer.reset(mark, mark_ch, mark_token);  
  return (T) super.deserialze(parser, clazz);  
}  
  
String value = lexer.scanFieldString(size_);  
if (lexer.matchStat == JSONScanner.NOT_MATCH) {  
    // 退出快速模式, 进入常规模式  
    lexer.reset(mark, mark_ch, mark_token);  
    return (T) super.deserialze(parser, clazz);  
}  
Size size = Size.valueOf(value);  
  
// ... ...  
  
// batch set  
Image image = new Image();  
image.setSize(size);  
image.setUri(uri);  
image.setTitle(title);  
image.setWidth(width);  
image.setHeight(height);  
  
return (T) image;  

3、使用asm避免反射 deserialize的时候,会使用asm来构造对象,并且做batch set,也就是说合并连续调用多个setter方法,而不是分散调用,这个能够提升性能。

4、对utf-8的json bytes,针对性使用优化的版本来转换编码。 这个类是com.alibaba.fastjson.util.UTF8Decoder,来源于JDK中的UTF8Decoder,但是它使用ThreadLocal Cache Buffer,避免转换时分配char[]的开销。 ThreadLocal Cache的实现是这个类com.alibaba.fastjson.util.ThreadLocalCache。第一次1k,如果不够,会增长,最多增长到128k。

Java代码

6、symbolTable算法

我们看xml或者javac的parser实现,经常会看到有一个这样的东西symbol table,它就是把一些经常使用的关键字缓存起来,在遍历char[]的时候,同时把hash计算好,通过这个hash值在hashtable中来获取缓存好的symbol,避免创建新的字符串对象。这种优化在fastjson里面用在key的读取,以及enum value的读取。这是也是parse性能优化的关键算法之一。

以下是摘抄自JSONScanner类中的代码,这段代码用于读取类型为enum的value。

Java代码

int hash = 0;  
for (;;) {  
    ch = buf[index++];  
    if (ch == '\"') {  
        bp = index;  
        this.ch = ch = buf[bp];  
        strVal = symbolTable.addSymbol(buf, start, index - start - 1, hash); // 通过symbolTable来获得缓存好的symbol,包括fieldName、enumValue  
        break;  
    }  
      
    hash = 31 * hash + ch; // 在token scan的过程中计算好hash  
  
    // ... ...  
}  

2.3.11 简易版的Fastjson

public static void main(String[] args) throws IllegalAccessException {
        // 创建需要转换的对象
        Person person = new Person();
        person.setName("小明");
        person.setAge(17);
        person.setSex("男");

        // 将对象转换为Json字符串
        String jsonString = MyJson.toJsonString(person);
        // 打印到控制台
        System.out.println(jsonString);
    }
    public static final String toJsonString(Object object) throws IllegalAccessException {
        // 获取反射对象
        Class<?> clazz = object.getClass();
        // 获取反射对象的所有属性,包括私有属性
        Field[] fields = clazz.getDeclaredFields();
        // 创建字符串类,便于拼接出JSON字符串
        StringBuffer stringBuffer = new StringBuffer();
        String str = "";
        stringBuffer.append("{");
        // 遍历所有属性
        for (Field field : fields) {

            // 第一次无须拼接逗号
            if (stringBuffer.length() != 1) {
                stringBuffer.append(",");
            }
// 给私有属性授权,以便能够对于私有属性操作
            field.setAccessible(true);

            stringBuffer.append("\"");
            stringBuffer.append(field.getName());
            stringBuffer.append("\"");

            stringBuffer.append(":");

            // 两种判断属性值是否为字符串类型的方式
            Boolean flag = field.getType().isInstance(str);
//            Boolean flag = field.getType().toString().equals("class java.lang.String");
            if (flag) { // 如果属性的类型为字符串,则给属性值加上双引号
                stringBuffer.append("\"");
                stringBuffer.append(field.get(object));
                stringBuffer.append("\"");
            } else {
                stringBuffer.append(field.get(object));
            }

        }
        stringBuffer.append("}");

        return stringBuffer.toString();
}

在这里插入图片描述 虽然造轮子的过程可能比较复杂,但我只需要编写一次,之后无论是什么对象我就可以直接使用。

3.参考文献

zhuanlan.zhihu.com/p/62763428 www.iflym.com/index.php/c…

4.总结

学习手稿

在学习过程中如果觉得电脑不足以让你有清楚的思路,那么可以把它写下来,这样会有意想不到的收获。

在这里插入图片描述 在这里插入图片描述

公众号

博客地址 blog.csdn.net/weixin_4156… 在这里插入图片描述

公众号

请关注 程序员面试之道

mp.weixin.qq.com/s/U2KvuYsRq…