• 来啊踩fastjson打印入参导致业务跑偏的坑


      线上代码对日志的记录,重要性自不必说。但是怎样记录日志也是有讲究的!
      日志可以直接在每个方法中进行日志记录,优点是想怎么记就怎么记,缺点是记日志的代码可能会超过你的业务代码,可读性急剧下降,这也是日志框架蓬勃发展的源头。
      日志也可以通过非业务代码侵入的形式进行记录,具体来说就是合作切面(aop)进行日志记录,好处自然是让业务更纯洁,缺点是运行性能会受到影响。
      当然了,前面这些都不是本文的主题。本文的主题是,当你用切面进行日志记录时,然后fastjson是如何把你的业务代码给干掉的。
      JsonObject.toJsonString(pj.getArgs()); 这是一个对入参的一个简单打印,至于使用fastjson的原因,自然是因为其输出字符的易读性。args是一个数组。那么这里会有什么问题?普通的参数自然是没有问题了。问题在于,fastjson转换对象为字符串时,报错了。为什么会这样?

      注: 本文bug所现问题入参类型示例为:

    public Object doSth(@RequestBody InBean in, HttpServletRequest request) {
         // do sth...
    }

      切面报错为:

    public Object deal(ProceedingJoinPoint pjp) throws Throwable {
          String argsStr = JSONObject.toJSONString(pjp.getArgs());
          logger.info("入参:{}", argsStr);
    }

    解析下fastjson源码过程:

    初步进入源码:

    // 直接调用json化方法
        public static String toJSONString(Object object) {
            return toJSONString(object, emptyFilters);
        }
        
    // 转到复杂参数的调用
        public static String toJSONString(Object object, SerializeFilter[] filters, SerializerFeature... features) {
            return toJSONString(object, SerializeConfig.globalInstance, filters, null, DEFAULT_GENERATE_FEATURE, features);
        }
        
    // 最终转换方法,1. 获取writer; 2. 处理filter; 3. 将数据定稿writer缓冲; 4. 返回缓冲数据给调用方
        public static String toJSONString(Object object, // 
                                          SerializeConfig config, // 
                                          SerializeFilter[] filters, // 
                                          String dateFormat, //
                                          int defaultFeatures, // 
                                          SerializerFeature... features) {
            SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);
    
            try {
                JSONSerializer serializer = new JSONSerializer(out, config);
                
                if (dateFormat != null && dateFormat.length() != 0) {
                    serializer.setDateFormat(dateFormat);
                    serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
                }
    
                if (filters != null) {
                    for (SerializeFilter filter : filters) {
                        serializer.addFilter(filter);
                    }
                }
    
                serializer.write(object);        // 最核心方法
    
                return out.toString();
            } finally {
                out.close();
            }
        }

    深入内部调用,追根究底:

        
    // 写方法的进一步操作
        public final void write(Object object) {
            if (object == null) {
                out.writeNull();
                return;
            }
    
            Class<?> clazz = object.getClass();
            ObjectSerializer writer = getObjectWriter(clazz);    // 关键,返回各种对应的序列化器, 如 ObjectArrayCodec
    
            try {
                writer.write(this, object, null, null, 0);        // 调用对应的序列化器进行处理
            } catch (IOException e) {
                throw new JSONException(e.getMessage(), e);
            }
        }
    
    // 追踪到在运行write方法时出的问题 ObjectArrayCodec
        public final void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
                                                                                                           throws IOException {
            SerializeWriter out = serializer.out;
    
            Object[] array = (Object[]) object;
    
            if (object == null) {
                out.writeNull(SerializerFeature.WriteNullListAsEmpty);
                return;
            }
    
            int size = array.length;
    
            int end = size - 1;
    
            if (end == -1) {
                out.append("[]");
                return;
            }
    
            SerialContext context = serializer.context;
            serializer.setContext(context, object, fieldName, 0);
    
            try {
                Class<?> preClazz = null;
                ObjectSerializer preWriter = null;
                out.append('[');
    
                if (out.isEnabled(SerializerFeature.PrettyFormat)) {
                    serializer.incrementIndent();
                    serializer.println();
                    for (int i = 0; i < size; ++i) {
                        if (i != 0) {
                            out.write(',');
                            serializer.println();
                        }
                        serializer.write(array[i]);
                    }
                    serializer.decrementIdent();
                    serializer.println();
                    out.write(']');
                    return;
                }
    
                for (int i = 0; i < end; ++i) {
                    Object item = array[i];
    
                    if (item == null) {
                        out.append("null,");
                    } else {
                        if (serializer.containsReference(item)) {
                            serializer.writeReference(item);
                        } else {
                            Class<?> clazz = item.getClass();
    
                            if (clazz == preClazz) {
                                preWriter.write(serializer, item, null, null, 0);
                            } else {
                                preClazz = clazz;
                                preWriter = serializer.getObjectWriter(clazz);
    
                                preWriter.write(serializer, item, null, null, 0);
                            }
                        }
                        out.append(',');
                    }
                }
    
                // 最后一个数组需要单独处理
                Object item = array[end];
    
                if (item == null) {
                    out.append("null]");
                } else {
                    if (serializer.containsReference(item)) {
                        serializer.writeReference(item);
                    } else {
                        serializer.writeWithFieldName(item, end);
                    }
                    out.append(']');
                }
            } finally {
                serializer.context = context;
            }
        }

    找到最后个出现问题的地方,发现再往下无法再跟踪:

    // 外部简单调用
    public final void writeWithFieldName(Object object, Object fieldName) {
        writeWithFieldName(object, fieldName, null, 0);
    }
    
    // 最终报错是在这里
        public final void writeWithFieldName(Object object, Object fieldName, Type fieldType, int fieldFeatures) {
            try {
                if (object == null) {
                    out.writeNull();
                    return;
                }
    
                Class<?> clazz = object.getClass();
    
                ObjectSerializer writer = getObjectWriter(clazz);        // ASMSerializer_2_RequestFacade 
    
                writer.write(this, object, fieldName, fieldType, fieldFeatures);    // 此处使用asm语言写的代码,无法debug, 但是会抛出 IllegalStateException 
            } catch (IOException e) {
                throw new JSONException(e.getMessage(), e);
            }
        }

    检查最后一次出现的问题,搜索出现的标记,找到动态生成asm代码的文件:

        
    // 动态生成代码 ASMSerializer_2_RequestFacade
        public JavaBeanSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) throws Exception {
            Class<?> clazz = beanInfo.beanType;
            if (clazz.isPrimitive()) {
                throw new JSONException("unsupportd class " + clazz.getName());
            }
    
            JSONType jsonType = clazz.getAnnotation(JSONType.class);
    
            FieldInfo[] unsortedGetters = beanInfo.fields;;
    
            for (FieldInfo fieldInfo : unsortedGetters) {
                if (fieldInfo.field == null //
                    && fieldInfo.method != null //
                    && fieldInfo.method.getDeclaringClass().isInterface()) {
                    return new JavaBeanSerializer(clazz);
                }
            }
    
            FieldInfo[] getters = beanInfo.sortedFields;
    
            boolean nativeSorted = beanInfo.sortedFields == beanInfo.fields;
    
            if (getters.length > 256) {
                return new JavaBeanSerializer(clazz);
            }
    
            for (FieldInfo getter : getters) {
                if (!ASMUtils.checkName(getter.getMember().getName())) {
                    return new JavaBeanSerializer(clazz);
                }
            }
    
            String className = "ASMSerializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
            String packageName = ASMSerializerFactory.class.getPackage().getName();
            String classNameType = packageName.replace('.', '/') + "/" + className;
            String classNameFull = packageName + "." + className;
    
            ClassWriter cw = new ClassWriter();
            cw.visit(V1_5 //
                     , ACC_PUBLIC + ACC_SUPER //
                     , classNameType //
                     , JavaBeanSerializer //
                     , new String[] { ObjectSerializer } //
            );
    
            for (FieldInfo fieldInfo : getters) {
                if (fieldInfo.fieldClass.isPrimitive() //
                    //|| fieldInfo.fieldClass.isEnum() //
                    || fieldInfo.fieldClass == String.class) {
                    continue;
                }
    
                new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_fieldType", "Ljava/lang/reflect/Type;") //
                                                                                                               .visitEnd();
    
                if (List.class.isAssignableFrom(fieldInfo.fieldClass)) {
                    new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_list_item_ser_",
                                    ObjectSerializer_desc) //
                                                           .visitEnd();
                }
    
                new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_ser_", ObjectSerializer_desc) //
                                                                                                            .visitEnd();
            }
    
            MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "<init>", "(" + desc(SerializeBeanInfo.class) + ")V", null, null);
            mw.visitVarInsn(ALOAD, 0);
            mw.visitVarInsn(ALOAD, 1);
            mw.visitMethodInsn(INVOKESPECIAL, JavaBeanSerializer, "<init>", "(" + desc(SerializeBeanInfo.class) + ")V");
    
            // init _asm_fieldType
            for (int i = 0; i < getters.length; ++i) {
                FieldInfo fieldInfo = getters[i];
                if (fieldInfo.fieldClass.isPrimitive() //
    //                || fieldInfo.fieldClass.isEnum() //
                    || fieldInfo.fieldClass == String.class) {
                    continue;
                }
    
                mw.visitVarInsn(ALOAD, 0);
    
                if (fieldInfo.method != null) {
                    mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldInfo.declaringClass)));
                    mw.visitLdcInsn(fieldInfo.method.getName());
                    mw.visitMethodInsn(INVOKESTATIC, type(ASMUtils.class), "getMethodType",
                                       "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Type;");
    
                } else {
                    mw.visitVarInsn(ALOAD, 0);
                    mw.visitLdcInsn(i);
                    mw.visitMethodInsn(INVOKESPECIAL, JavaBeanSerializer, "getFieldType", "(I)Ljava/lang/reflect/Type;");
                }
    
                mw.visitFieldInsn(PUTFIELD, classNameType, fieldInfo.name + "_asm_fieldType", "Ljava/lang/reflect/Type;");
            }
    
            mw.visitInsn(RETURN);
            mw.visitMaxs(4, 4);
            mw.visitEnd();
    
            boolean DisableCircularReferenceDetect = false;
            if (jsonType != null) {
                for (SerializerFeature featrues : jsonType.serialzeFeatures()) {
                    if (featrues == SerializerFeature.DisableCircularReferenceDetect) {
                        DisableCircularReferenceDetect = true;
                        break;
                    }
                }
            }
    
            // 0 write
            // 1 writeNormal
            // 2 writeNonContext
            for (int i = 0; i < 3; ++i) {
                String methodName;
                boolean nonContext = DisableCircularReferenceDetect;
                boolean writeDirect = false;
                if (i == 0) {
                    methodName = "write";
                    writeDirect = true;
                } else if (i == 1) {
                    methodName = "writeNormal";
                } else {
                    writeDirect = true;
                    nonContext = true;
                    methodName = "writeDirectNonContext";
                }
    
                Context context = new Context(getters, beanInfo, classNameType, writeDirect,
                                              nonContext);
    
                mw = new MethodWriter(cw, //
                                      ACC_PUBLIC, //
                                      methodName, //
                                      "(L" + JSONSerializer
                                                  + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V", //
                                      null, //
                                      new String[] { "java/io/IOException" } //
                );
    
                mw.visitVarInsn(ALOAD, Context.serializer);
                mw.visitFieldInsn(GETFIELD, JSONSerializer, "out", SerializeWriter_desc);
                mw.visitVarInsn(ASTORE, context.var("out"));
    
                if ((!nativeSorted) //
                    && !context.writeDirect) {
    
                    if (jsonType == null || jsonType.alphabetic()) {
                        Label _else = new Label();
    
                        mw.visitVarInsn(ALOAD, context.var("out"));
                        mw.visitMethodInsn(INVOKEVIRTUAL, SerializeWriter, "isSortField", "()Z");
    
                        mw.visitJumpInsn(IFNE, _else);
                        mw.visitVarInsn(ALOAD, 0);
                        mw.visitVarInsn(ALOAD, 1);
                        mw.visitVarInsn(ALOAD, 2);
                        mw.visitVarInsn(ALOAD, 3);
                        mw.visitVarInsn(ALOAD, 4);
                        mw.visitVarInsn(ILOAD, 5);
                        mw.visitMethodInsn(INVOKEVIRTUAL, classNameType,
                                           "writeUnsorted", "(L" + JSONSerializer
                                                            + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
                        mw.visitInsn(RETURN);
    
                        mw.visitLabel(_else);
                    }
                }
    
                // isWriteDoubleQuoteDirect
                if (context.writeDirect && !nonContext) {
                    Label _direct = new Label();
                    Label _directElse = new Label();
    
                    mw.visitVarInsn(ALOAD, 0);
                    mw.visitVarInsn(ALOAD, Context.serializer);
                    mw.visitMethodInsn(INVOKEVIRTUAL, JavaBeanSerializer, "writeDirect", "(L" + JSONSerializer + ";)Z");
                    mw.visitJumpInsn(IFNE, _directElse);
    
                    mw.visitVarInsn(ALOAD, 0);
                    mw.visitVarInsn(ALOAD, 1);
                    mw.visitVarInsn(ALOAD, 2);
                    mw.visitVarInsn(ALOAD, 3);
                    mw.visitVarInsn(ALOAD, 4);
                    mw.visitVarInsn(ILOAD, 5);
                    mw.visitMethodInsn(INVOKEVIRTUAL, classNameType,
                                       "writeNormal", "(L" + JSONSerializer
                                                      + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
                    mw.visitInsn(RETURN);
    
                    mw.visitLabel(_directElse);
                    mw.visitVarInsn(ALOAD, context.var("out"));
                    mw.visitLdcInsn(SerializerFeature.DisableCircularReferenceDetect.mask);
                    mw.visitMethodInsn(INVOKEVIRTUAL, SerializeWriter, "isEnabled", "(I)Z");
                    mw.visitJumpInsn(IFEQ, _direct);
    
                    mw.visitVarInsn(ALOAD, 0);
                    mw.visitVarInsn(ALOAD, 1);
                    mw.visitVarInsn(ALOAD, 2);
                    mw.visitVarInsn(ALOAD, 3);
                    mw.visitVarInsn(ALOAD, 4);
                    mw.visitVarInsn(ILOAD, 5);
                    mw.visitMethodInsn(INVOKEVIRTUAL, classNameType, "writeDirectNonContext",
                                       "(L" + JSONSerializer + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
                    mw.visitInsn(RETURN);
    
                    mw.visitLabel(_direct);
                }
    
                mw.visitVarInsn(ALOAD, Context.obj); // obj
                mw.visitTypeInsn(CHECKCAST, type(clazz)); // serializer
                mw.visitVarInsn(ASTORE, context.var("entity")); // obj
                generateWriteMethod(clazz, mw, getters, context);
                mw.visitInsn(RETURN);
                mw.visitMaxs(7, context.variantIndex + 2);
                mw.visitEnd();
            }
    
            if (!nativeSorted) {
                // sortField support
                Context context = new Context(getters, beanInfo, classNameType, false,
                                              DisableCircularReferenceDetect);
    
                mw = new MethodWriter(cw, ACC_PUBLIC, "writeUnsorted",
                                      "(L" + JSONSerializer + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V",
                                      null, new String[] { "java/io/IOException" });
    
                mw.visitVarInsn(ALOAD, Context.serializer);
                mw.visitFieldInsn(GETFIELD, JSONSerializer, "out", SerializeWriter_desc);
                mw.visitVarInsn(ASTORE, context.var("out"));
    
                mw.visitVarInsn(ALOAD, Context.obj); // obj
                mw.visitTypeInsn(CHECKCAST, type(clazz)); // serializer
                mw.visitVarInsn(ASTORE, context.var("entity")); // obj
    
                generateWriteMethod(clazz, mw, unsortedGetters, context);
    
                mw.visitInsn(RETURN);
                mw.visitMaxs(7, context.variantIndex + 2);
                mw.visitEnd();
            }
    
            // 0 writeAsArray
            // 1 writeAsArrayNormal
            // 2 writeAsArrayNonContext
            for (int i = 0; i < 3; ++i) {
                String methodName;
                boolean nonContext = DisableCircularReferenceDetect;
                boolean writeDirect = false;
                if (i == 0) {
                    methodName = "writeAsArray";
                    writeDirect = true;
                } else if (i == 1) {
                    methodName = "writeAsArrayNormal";
                } else {
                    writeDirect = true;
                    nonContext = true;
                    methodName = "writeAsArrayNonContext";
                }
    
                Context context = new Context(getters, beanInfo, classNameType, writeDirect,
                                              nonContext);
    
                mw = new MethodWriter(cw, ACC_PUBLIC, methodName,
                                      "(L" + JSONSerializer + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V",
                                      null, new String[] { "java/io/IOException" });
    
                mw.visitVarInsn(ALOAD, Context.serializer);
                mw.visitFieldInsn(GETFIELD, JSONSerializer, "out", SerializeWriter_desc);
                mw.visitVarInsn(ASTORE, context.var("out"));
    
                mw.visitVarInsn(ALOAD, Context.obj); // obj
                mw.visitTypeInsn(CHECKCAST, type(clazz)); // serializer
                mw.visitVarInsn(ASTORE, context.var("entity")); // obj
                generateWriteAsArray(clazz, mw, getters, context);
                mw.visitInsn(RETURN);
                mw.visitMaxs(7, context.variantIndex + 2);
                mw.visitEnd();
            }
    
            byte[] code = cw.toByteArray();
    
            Class<?> exampleClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
            Constructor<?> constructor = exampleClass.getConstructor(SerializeBeanInfo.class);
            Object instance = constructor.newInstance(beanInfo);
    
            return (JavaBeanSerializer) instance;
        }
    View Code

    再回退到上一步,查找调用链路:

    // 如果没有找到,就创建一个类
        public ObjectSerializer getObjectWriter(Class<?> clazz) {
            return getObjectWriter(clazz, true);
        }
        
    // classLoader 多次加载
        private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
            ObjectSerializer writer = serializers.get(clazz);
    
            if (writer == null) {
                try {
                    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                    for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
                        if (!(o instanceof AutowiredObjectSerializer)) {
                            continue;
                        }
    
                        AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
                        for (Type forType : autowired.getAutowiredFor()) {
                            put(forType, autowired);
                        }
                    }
                } catch (ClassCastException ex) {
                    // skip
                }
    
                writer = serializers.get(clazz);
            }
    
            if (writer == null) {
                final ClassLoader classLoader = JSON.class.getClassLoader();
                if (classLoader != Thread.currentThread().getContextClassLoader()) {
                    try {
                        for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
    
                            if (!(o instanceof AutowiredObjectSerializer)) {
                                continue;
                            }
    
                            AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
                            for (Type forType : autowired.getAutowiredFor()) {
                                put(forType, autowired);
                            }
                        }
                    } catch (ClassCastException ex) {
                        // skip
                    }
    
                    writer = serializers.get(clazz);
                }
            }
            
            if (writer == null) {
                if (Map.class.isAssignableFrom(clazz)) {
                    put(clazz, MapSerializer.instance);
                } else if (List.class.isAssignableFrom(clazz)) {
                    put(clazz, ListSerializer.instance);
                } else if (Collection.class.isAssignableFrom(clazz)) {
                    put(clazz, CollectionCodec.instance);
                } else if (Date.class.isAssignableFrom(clazz)) {
                    put(clazz, DateCodec.instance);
                } else if (JSONAware.class.isAssignableFrom(clazz)) {
                    put(clazz, JSONAwareSerializer.instance);
                } else if (JSONSerializable.class.isAssignableFrom(clazz)) {
                    put(clazz, JSONSerializableSerializer.instance);
                } else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
                    put(clazz, MiscCodec.instance);
                } else if (clazz.isEnum() || (clazz.getSuperclass() != null && clazz.getSuperclass().isEnum())) {
                    JSONType jsonType = clazz.getAnnotation(JSONType.class);
                    if (jsonType != null && jsonType.serializeEnumAsJavaBean()) {
                        put(clazz, createJavaBeanSerializer(clazz));
                    } else {
                        put(clazz, EnumSerializer.instance);
                    }
                } else if (clazz.isArray()) {
                    Class<?> componentType = clazz.getComponentType();
                    ObjectSerializer compObjectSerializer = getObjectWriter(componentType);
                    put(clazz, new ArraySerializer(componentType, compObjectSerializer));
                } else if (Throwable.class.isAssignableFrom(clazz)) {
                    SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy);
                    beanInfo.features |= SerializerFeature.WriteClassName.mask;
                    put(clazz, new JavaBeanSerializer(beanInfo));
                } else if (TimeZone.class.isAssignableFrom(clazz) || Map.Entry.class.isAssignableFrom(clazz)) {
                    put(clazz, MiscCodec.instance);
                } else if (Appendable.class.isAssignableFrom(clazz)) {
                    put(clazz, AppendableSerializer.instance);
                } else if (Charset.class.isAssignableFrom(clazz)) {
                    put(clazz, ToStringSerializer.instance);
                } else if (Enumeration.class.isAssignableFrom(clazz)) {
                    put(clazz, EnumerationSerializer.instance);
                } else if (Calendar.class.isAssignableFrom(clazz) //
                        || XMLGregorianCalendar.class.isAssignableFrom(clazz)) {
                    put(clazz, CalendarCodec.instance);
                } else if (Clob.class.isAssignableFrom(clazz)) {
                    put(clazz, ClobSeriliazer.instance);
                } else if (TypeUtils.isPath(clazz)) {
                    put(clazz, ToStringSerializer.instance);
                } else if (Iterator.class.isAssignableFrom(clazz)) {
                    put(clazz, MiscCodec.instance);
                } else {
                    String className = clazz.getName();
                    if (className.startsWith("java.awt.") //
                        && AwtCodec.support(clazz) //
                    ) {
                        // awt
                        if (!awtError) {
                            try {
                                put(Class.forName("java.awt.Color"), AwtCodec.instance);
                                put(Class.forName("java.awt.Font"), AwtCodec.instance);
                                put(Class.forName("java.awt.Point"), AwtCodec.instance);
                                put(Class.forName("java.awt.Rectangle"), AwtCodec.instance);
                            } catch (Throwable e) {
                                awtError = true;
                                // skip
                            }
                        }
                        return  AwtCodec.instance;
                    }
                    
                    // jdk8
                    if ((!jdk8Error) //
                        && (className.startsWith("java.time.") //
                            || className.startsWith("java.util.Optional") //
                        )) {
                        try {
                            put(Class.forName("java.time.LocalDateTime"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.LocalDate"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.LocalTime"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.ZonedDateTime"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.OffsetDateTime"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.OffsetTime"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.ZoneOffset"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.ZoneRegion"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.Period"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.Duration"), Jdk8DateCodec.instance);
                            put(Class.forName("java.time.Instant"), Jdk8DateCodec.instance);
    
                            put(Class.forName("java.util.Optional"), OptionalCodec.instance);
                            put(Class.forName("java.util.OptionalDouble"), OptionalCodec.instance);
                            put(Class.forName("java.util.OptionalInt"), OptionalCodec.instance);
                            put(Class.forName("java.util.OptionalLong"), OptionalCodec.instance);
                            
                            writer = serializers.get(clazz);
                            if (writer != null) {
                                return writer;
                            }
                        } catch (Throwable e) {
                            // skip
                            jdk8Error = true;
                        }
                    }
                    
                    if ((!oracleJdbcError) //
                        && className.startsWith("oracle.sql.")) {
                        try {
                            put(Class.forName("oracle.sql.DATE"), DateCodec.instance);
                            put(Class.forName("oracle.sql.TIMESTAMP"), DateCodec.instance);
                            
                            writer = serializers.get(clazz);
                            if (writer != null) {
                                return writer;
                            }
                        } catch (Throwable e) {
                            // skip
                            oracleJdbcError = true;
                        }
                    }
                    
                    if ((!springfoxError) //
                        && className.equals("springfox.documentation.spring.web.json.Json")) {
                        try {
                            put(Class.forName("springfox.documentation.spring.web.json.Json"), //
                                SwaggerJsonSerializer.instance);
                            
                            writer = serializers.get(clazz);
                            if (writer != null) {
                                return writer;
                            }
                        } catch (ClassNotFoundException e) {
                            // skip
                            springfoxError = true;
                        }
                    }
    
                    if ((!guavaError) //
                            && className.startsWith("com.google.common.collect.")) {
                        try {
                            put(Class.forName("com.google.common.collect.HashMultimap"), //
                                    GuavaCodec.instance);
                            put(Class.forName("com.google.common.collect.LinkedListMultimap"), //
                                    GuavaCodec.instance);
                            put(Class.forName("com.google.common.collect.ArrayListMultimap"), //
                                    GuavaCodec.instance);
                            put(Class.forName("com.google.common.collect.TreeMultimap"), //
                                    GuavaCodec.instance);
    
                            writer = serializers.get(clazz);
                            if (writer != null) {
                                return writer;
                            }
                        } catch (ClassNotFoundException e) {
                            // skip
                            guavaError = true;
                        }
                    }
    
                    if (className.equals("net.sf.json.JSONNull")) {
                        try {
                            put(Class.forName("net.sf.json.JSONNull"), //
                                    MiscCodec.instance);
                        } catch (ClassNotFoundException e) {
                            // skip
                        }
                        writer = serializers.get(clazz);
                        if (writer != null) {
                            return writer;
                        }
                    }
    
                    if (TypeUtils.isProxy(clazz)) {
                        Class<?> superClazz = clazz.getSuperclass();
    
                        ObjectSerializer superWriter = getObjectWriter(superClazz);
                        put(clazz, superWriter);
                        return superWriter;
                    }
    
                    // asm创建类是在这里生效的
                    if (create) {
                        put(clazz, createJavaBeanSerializer(clazz));
                    }
                }
    
                writer = serializers.get(clazz);
            }
            return writer;
        }

    往前追踪,发现入口:

    // TypeUtils, 获取所有get方法。
        public static List<FieldInfo> computeGetters(Class<?> clazz, //
                                                     JSONType jsonType, //
                                                     Map<String, String> aliasMap, //
                                                     Map<String, Field> fieldCacheMap, //
                                                     boolean sorted, //
                                                     PropertyNamingStrategy propertyNamingStrategy //
        ) {
            Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>();
    
            for (Method method : clazz.getMethods()) {
                String methodName = method.getName();
                int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
                String label = null;
    
                if (Modifier.isStatic(method.getModifiers())) {
                    continue;
                }
    
                if (method.getReturnType().equals(Void.TYPE)) {
                    continue;
                }
    
                if (method.getParameterTypes().length != 0) {
                    continue;
                }
    
                if (method.getReturnType() == ClassLoader.class) {
                    continue;
                }
    
                if (method.getName().equals("getMetaClass")
                    && method.getReturnType().getName().equals("groovy.lang.MetaClass")) {
                    continue;
                }
    
                JSONField annotation = method.getAnnotation(JSONField.class);
    
                if (annotation == null) {
                    annotation = getSuperMethodAnnotation(clazz, method);
                }
    
                if (annotation != null) {
                    if (!annotation.serialize()) {
                        continue;
                    }
    
                    ordinal = annotation.ordinal();
                    serialzeFeatures = SerializerFeature.of(annotation.serialzeFeatures());
                    parserFeatures = Feature.of(annotation.parseFeatures());
    
                    if (annotation.name().length() != 0) {
                        String propertyName = annotation.name();
    
                        if (aliasMap != null) {
                            propertyName = aliasMap.get(propertyName);
                            if (propertyName == null) {
                                continue;
                            }
                        }
    
                        FieldInfo fieldInfo = new FieldInfo(propertyName, method, null, clazz, null, ordinal,
                                                            serialzeFeatures, parserFeatures, annotation, null, label);
                        fieldInfoMap.put(propertyName, fieldInfo);
                        continue;
                    }
    
                    if (annotation.label().length() != 0) {
                        label = annotation.label();
                    }
                }
    
                if (methodName.startsWith("get")) {
                    if (methodName.length() < 4) {
                        continue;
                    }
    
                    if (methodName.equals("getClass")) {
                        continue;
                    }
    
                    if (methodName.equals("getDeclaringClass") && clazz.isEnum()) {
                        continue;
                    }
    
                    char c3 = methodName.charAt(3);
    
                    String propertyName;
                    if (Character.isUpperCase(c3) //
                        || c3 > 512 // for unicode method name
                    ) {
                       if (compatibleWithJavaBean) {
                            propertyName = decapitalize(methodName.substring(3));
                        } else {
                            propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
                        }
                        propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName,  propertyName,3); 
                    } else if (c3 == '_') {
                        propertyName = methodName.substring(4);
                    } else if (c3 == 'f') {
                        propertyName = methodName.substring(3);
                    } else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {
                        propertyName = decapitalize(methodName.substring(3));
                    } else {
                        continue;
                    }
    
                    boolean ignore = isJSONTypeIgnore(clazz, propertyName);
    
                    if (ignore) {
                        continue;
                    }
                    //假如bean的field很多的情况一下,轮询时将大大降低效率
                    Field field = ParserConfig.getFieldFromCache(propertyName, fieldCacheMap);
                    
                    if (field == null && propertyName.length() > 1) {
                        char ch = propertyName.charAt(1);
                        if (ch >= 'A' && ch <= 'Z') {
                            String javaBeanCompatiblePropertyName = decapitalize(methodName.substring(3));
                            field = ParserConfig.getFieldFromCache(javaBeanCompatiblePropertyName, fieldCacheMap);
                        }
                    }
                    
                    JSONField fieldAnnotation = null;
                    if (field != null) {
                        fieldAnnotation = field.getAnnotation(JSONField.class);
    
                        if (fieldAnnotation != null) {
                            if (!fieldAnnotation.serialize()) {
                                continue;
                            }
    
                            ordinal = fieldAnnotation.ordinal();
                            serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
                            parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
    
                            if (fieldAnnotation.name().length() != 0) {
                                propertyName = fieldAnnotation.name();
    
                                if (aliasMap != null) {
                                    propertyName = aliasMap.get(propertyName);
                                    if (propertyName == null) {
                                        continue;
                                    }
                                }
                            }
    
                            if (fieldAnnotation.label().length() != 0) {
                                label = fieldAnnotation.label();
                            }
                        }
                    }
    
                    if (aliasMap != null) {
                        propertyName = aliasMap.get(propertyName);
                        if (propertyName == null) {
                            continue;
                        }
                    }
                    
                    if (propertyNamingStrategy != null) {
                        propertyName = propertyNamingStrategy.translate(propertyName);
                    }
    
                    FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
                                                        annotation, fieldAnnotation, label);
                    fieldInfoMap.put(propertyName, fieldInfo);
                }
    
                if (methodName.startsWith("is")) {
                    if (methodName.length() < 3) {
                        continue;
                    }
    
                    if (method.getReturnType() != Boolean.TYPE
                            && method.getReturnType() != Boolean.class) {
                        continue;
                    }
    
                    char c2 = methodName.charAt(2);
    
                    String propertyName;
                    if (Character.isUpperCase(c2)) {
                        if (compatibleWithJavaBean) {
                            propertyName = decapitalize(methodName.substring(2));
                        } else {
                            propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
                        }
                        propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName,  propertyName,2); 
                    } else if (c2 == '_') {
                        propertyName = methodName.substring(3);
                    } else if (c2 == 'f') {
                        propertyName = methodName.substring(2);
                    } else {
                        continue;
                    }
    
                    Field field = ParserConfig.getFieldFromCache(propertyName,fieldCacheMap);
    
                    if (field == null) {
                        field = ParserConfig.getFieldFromCache(methodName,fieldCacheMap); 
                    }
    
                    JSONField fieldAnnotation = null;
                    if (field != null) {
                        fieldAnnotation = field.getAnnotation(JSONField.class);
    
                        if (fieldAnnotation != null) {
                            if (!fieldAnnotation.serialize()) {
                                continue;
                            }
    
                            ordinal = fieldAnnotation.ordinal();
                            serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
                            parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
                            
                            if (fieldAnnotation.name().length() != 0) {
                                propertyName = fieldAnnotation.name();
    
                                if (aliasMap != null) {
                                    propertyName = aliasMap.get(propertyName);
                                    if (propertyName == null) {
                                        continue;
                                    }
                                }
                            }
    
                            if (fieldAnnotation.label().length() != 0) {
                                label = fieldAnnotation.label();
                            }
                        }
                    }
    
                    if (aliasMap != null) {
                        propertyName = aliasMap.get(propertyName);
                        if (propertyName == null) {
                            continue;
                        }
                    }
                    
                    if (propertyNamingStrategy != null) {
                        propertyName = propertyNamingStrategy.translate(propertyName);
                    }
    
                    //优先选择get
                    if (fieldInfoMap.containsKey(propertyName)) {
                        continue;
                    }
    
                    FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
                                                        annotation, fieldAnnotation, label);
                    fieldInfoMap.put(propertyName, fieldInfo);
                }
            }
    
            for (Field field : clazz.getFields()) {
                if (Modifier.isStatic(field.getModifiers())) {
                    continue;
                }
    
                JSONField fieldAnnotation = field.getAnnotation(JSONField.class);
    
                int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
                String propertyName = field.getName();
                String label = null;
                if (fieldAnnotation != null) {
                    if (!fieldAnnotation.serialize()) {
                        continue;
                    }
    
                    ordinal = fieldAnnotation.ordinal();
                    serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
                    parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
    
                    if (fieldAnnotation.name().length() != 0) {
                        propertyName = fieldAnnotation.name();
                    }
    
                    if (fieldAnnotation.label().length() != 0) {
                        label = fieldAnnotation.label();
                    }
                }
    
                if (aliasMap != null) {
                    propertyName = aliasMap.get(propertyName);
                    if (propertyName == null) {
                        continue;
                    }
                }
                
                if (propertyNamingStrategy != null) {
                    propertyName = propertyNamingStrategy.translate(propertyName);
                }
    
                if (!fieldInfoMap.containsKey(propertyName)) {
                    FieldInfo fieldInfo = new FieldInfo(propertyName, null, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
                                                        null, fieldAnnotation, label);
                    fieldInfoMap.put(propertyName, fieldInfo);
                }
            }
    
            List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>();
    
            boolean containsAll = false;
            String[] orders = null;
    
            JSONType annotation = clazz.getAnnotation(JSONType.class);
            if (annotation != null) {
                orders = annotation.orders();
    
                if (orders != null && orders.length == fieldInfoMap.size()) {
                    containsAll = true;
                    for (String item : orders) {
                        if (!fieldInfoMap.containsKey(item)) {
                            containsAll = false;
                            break;
                        }
                    }
                } else {
                    containsAll = false;
                }
            }
    
            if (containsAll) {
                for (String item : orders) {
                    FieldInfo fieldInfo = fieldInfoMap.get(item);
                    fieldInfoList.add(fieldInfo);
                }
            } else {
                for (FieldInfo fieldInfo : fieldInfoMap.values()) {
                    fieldInfoList.add(fieldInfo);
                }
    
                if (sorted) {
                    Collections.sort(fieldInfoList);
                }
            }
    
            return fieldInfoList;
        }
    
    
    // SerializeConfig, 创建java bean.
    
        public ObjectSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) {
            JSONType jsonType = beanInfo.jsonType;
            
            if (jsonType != null) {
                Class<?> serializerClass = jsonType.serializer();
                if (serializerClass != Void.class) {
                    try {
                        Object seralizer = serializerClass.newInstance();
                        if (seralizer instanceof ObjectSerializer) {
                            return (ObjectSerializer) seralizer;
                        }
                    } catch (Throwable e) {
                        // skip
                    }
                }
                
                if (jsonType.asm() == false) {
                    asm = false;
                }
            }
            
            Class<?> clazz = beanInfo.beanType;
            if (!Modifier.isPublic(beanInfo.beanType.getModifiers())) {
                return new JavaBeanSerializer(beanInfo);
            }
    
            boolean asm = this.asm;
    
            if (asm && asmFactory.classLoader.isExternalClass(clazz)
                    || clazz == Serializable.class || clazz == Object.class) {
                asm = false;
            }
    
            if (asm && !ASMUtils.checkName(clazz.getSimpleName())) {
                asm = false;
            }
            
            if (asm) {
                for(FieldInfo fieldInfo : beanInfo.fields){
                    Field field = fieldInfo.field;
                    if (field != null && !field.getType().equals(fieldInfo.fieldClass)) {
                        asm = false;
                        break;
                    }
    
                    Method method = fieldInfo.method;
                    if (method != null && !method.getReturnType().equals(fieldInfo.fieldClass)) {
                        asm = false;
                        break;
                    }
    
                    JSONField annotation = fieldInfo.getAnnotation();
                    
                    if (annotation == null) {
                        continue;
                    }
                    if ((!ASMUtils.checkName(annotation.name())) //
                            || annotation.format().length() != 0
                            || annotation.jsonDirect()
                            || annotation.serializeUsing() != Void.class
                            ) {
                        asm = false;
                        break;
                    }
                }
            }
            
            if (asm) {
                try {
                    ObjectSerializer asmSerializer = createASMSerializer(beanInfo);
                    if (asmSerializer != null) {
                        return asmSerializer;
                    }
                } catch (ClassFormatError e) {
                    // skip
                } catch (ClassCastException e) {
                    // skip
                } catch (Throwable e) {
                    throw new JSONException("create asm serializer error, class "
                            + clazz, e);
                }
            }
    
            return new JavaBeanSerializer(beanInfo);
        }
    View Code

    使用asmClassLoader加载动态类:

    // 创建asmSerializer, 最后调用classloader加载类
        private final JavaBeanSerializer createASMSerializer(SerializeBeanInfo beanInfo) throws Exception {
            JavaBeanSerializer serializer = asmFactory.createJavaBeanSerializer(beanInfo);
            
            for (int i = 0; i < serializer.sortedGetters.length; ++i) {
                FieldSerializer fieldDeser = serializer.sortedGetters[i];
                Class<?> fieldClass = fieldDeser.fieldInfo.fieldClass;
                if (fieldClass.isEnum()) {
                    ObjectSerializer fieldSer = this.getObjectWriter(fieldClass);
                    if (!(fieldSer instanceof EnumSerializer)) {
                        serializer.writeDirect = false;
                    }
                }
            }
         
            return serializer;
        }

      最后的代码报错是:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false),由于是写在动态生成的代码文件中,比较难查,解决问题已足够,不再深究。

      理解这里的原因后,再来看问题,就简单多了,排除掉不支持转换的类或者使用其他方式获取想要的参数。
      思考:如果让自己来设计框架,也需要考虑得更多;在用别人的框架的时候,也以一个开发者的角度考虑问题,如果不想考虑,可以直接参考其源码,发现其中的蹊跷;另外,测试一定是必不可少的,你可以借助测试部门,也可以自行测试,但是一定要考虑全面;最后,在做一些公用方法的变动时,一定不要太大意,宁可相信自己不无知,也不要相信代码一定不会出错,有条件情况下,尽可以使用try catch将其包裹,这样,在出问题的时候,也能迅速定位。
      题外话:错误一定是会犯的,但是不要关键时候犯。

  • 相关阅读:
    kubernetes入门(03)kubernetes的基本概念
    洛谷P3245 [HNOI2016]大数(莫队)
    洛谷P4462 [CQOI2018]异或序列(莫队)
    cf997C. Sky Full of Stars(组合数 容斥)
    cf1121F. Compress String(后缀自动机)
    洛谷P4704 太极剑(乱搞)
    洛谷P4926 [1007]倍杀测量者(差分约束)
    洛谷P4590 [TJOI2018]游园会(状压dp LCS)
    洛谷P4588 [TJOI2018]数学计算(线段树)
    洛谷P4592 [TJOI2018]异或(可持久化01Trie)
  • 原文地址:https://www.cnblogs.com/yougewe/p/9433438.html
Copyright © 2020-2023  润新知