来啊踩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;
- }
再回退到上一步,查找调用链路:
- // 如果没有找到,就创建一个类
- 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);
- }
使用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将其包裹,这样,在出问题的时候,也能迅速定位。
题外话:错误一定是会犯的,但是不要关键时候犯。
来啊踩fastjson打印入参导致业务跑偏的坑的更多相关文章
- dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析
问题描述&模拟 线上登录接口,通过监控查看,有类型转换异常,具体报错如下图 此报错信息是dubbo consumer端显示,且登录大部分是正常,有少量部分会报类型转换异常,同事通过更换方法名+ ...
- 【C语言】 strlen()入参空指针导致段错误
背景: 在工作中调试sqlite3相关代码的时候,调用printf()打印sqlite3_exec()的执行日志:因为sqlite3_exec()保存日志的参数传入时为NULL,且没有执行错误,所以再 ...
- MyBatis版本升级导致OffsetDateTime入参解析异常问题复盘
背景 最近有一个数据统计服务需要升级SpringBoot的版本,由1.5.x.RELEASE直接升级到2.3.0.RELEASE,考虑到没有用到SpringBoot的内建SPI,升级过程算是顺利.但是 ...
- 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
- Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志
其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...
- Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId
一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...
- SpringBoot2 参数管理实践,入参出参与校验
一.参数管理 在编程系统中,为了能写出良好的代码,会根据是各种设计模式.原则.约束等去规范代码,从而提高代码的可读性.复用性.可修改,实际上个人觉得,如果写出的代码很好,即别人修改也无法破坏原作者的思 ...
- 从输出日志中提取接口的入参和返回做为用例导入到excel中
1 背景 接口用例已经在项目中的yml文件中编写,但是yml文件不能做为交付文档用,本文对工作中从接口输出日志中提取用例信息,并导入到excel文件中做了总些 2 工具 idea,notepad+ ...
- Oracle存储过程入参传入List集合的小例子
第一步:创建一个对象类型 create or replace type STUDENT as object( id ), name ), age ) ); / 第二步:创建一个数组类型 (任意选择下面 ...
随机推荐
- srs之深入浅出看流媒体
本文转载:https://blog.csdn.net/zjqlovell/article/details/50786040 CDN这几年爆炸式增长,带宽提速是根源,而HTTP始终还是那个屌样,因此目前 ...
- 自适应Web主页
HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- Linux下docker报错syntax error:unexpected protocol at end of statement
---恢复内容开始--- [Linux]Shell脚本“syntax error: unexpected end of file”原因及处理 :::https://blog.csdn.net/u013 ...
- EasyPR源码剖析(7):车牌判断之SVM
前面的文章中我们主要介绍了车牌定位的相关技术,但是定位出来的相关区域可能并非是真实的车牌区域,EasyPR通过SVM支持向量机,一种机器学习算法来判定截取的图块是否是真的“车牌”,本节主要对相关的技术 ...
- eclipse创建scrapy项目
1. 您必须创建一个新的Scrapy项目. 进入您打算存储代码的目录中(比如否F:/demo),运行下列命令: scrapy startproject tutorial 2.在eclipse中创建一个 ...
- net::ERR_CONNECTION_RESET 问题排查
后台服务器代码有问题 实体不对称,导致映射不对
- 关于sql优化的一些点
慢查询日志 参考:https://www.cnblogs.com/saneri/p/6656161.html 查询是否开启慢查询日志: show variables like '%slow_query ...
- dubbo-2.5.6优雅停机研究
不优雅的停机: 当进程存在正在运行的线程时,如果直接执行kill -9 pid时,那么这个正在执行的线程被中断,就好像一个机器运行中突然遭遇断电的情况,所导致的结果是造成服务调用的消费端报错,也有可能 ...
- MongoDB学习记录(四) - MongoDB的"增查改删"操作之"改"
更新文档主要有以下几种方法: db.collection.updateOne(filter, update, options) db.collection.updateMany(filter, upd ...
- 《团队-爬取豆瓣top250-开发文档》
项目托管平台地址:https://github.com/gengwenhao/GetTop250.git 本项目能够爬取豆瓣电影TOP250并向用户展示电影的排名,分数等