线上代码对日志的记录,重要性自不必说。但是怎样记录日志也是有讲究的!
  日志可以直接在每个方法中进行日志记录,优点是想怎么记就怎么记,缺点是记日志的代码可能会超过你的业务代码,可读性急剧下降,这也是日志框架蓬勃发展的源头。
  日志也可以通过非业务代码侵入的形式进行记录,具体来说就是合作切面(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打印入参导致业务跑偏的坑的更多相关文章

  1. dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析

    问题描述&模拟 线上登录接口,通过监控查看,有类型转换异常,具体报错如下图 此报错信息是dubbo consumer端显示,且登录大部分是正常,有少量部分会报类型转换异常,同事通过更换方法名+ ...

  2. 【C语言】 strlen()入参空指针导致段错误

    背景: 在工作中调试sqlite3相关代码的时候,调用printf()打印sqlite3_exec()的执行日志:因为sqlite3_exec()保存日志的参数传入时为NULL,且没有执行错误,所以再 ...

  3. MyBatis版本升级导致OffsetDateTime入参解析异常问题复盘

    背景 最近有一个数据统计服务需要升级SpringBoot的版本,由1.5.x.RELEASE直接升级到2.3.0.RELEASE,考虑到没有用到SpringBoot的内建SPI,升级过程算是顺利.但是 ...

  4. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  5. Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...

  6. Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId

    一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...

  7. SpringBoot2 参数管理实践,入参出参与校验

    一.参数管理 在编程系统中,为了能写出良好的代码,会根据是各种设计模式.原则.约束等去规范代码,从而提高代码的可读性.复用性.可修改,实际上个人觉得,如果写出的代码很好,即别人修改也无法破坏原作者的思 ...

  8. 从输出日志中提取接口的入参和返回做为用例导入到excel中

    1  背景 接口用例已经在项目中的yml文件中编写,但是yml文件不能做为交付文档用,本文对工作中从接口输出日志中提取用例信息,并导入到excel文件中做了总些 2  工具 idea,notepad+ ...

  9. Oracle存储过程入参传入List集合的小例子

    第一步:创建一个对象类型 create or replace type STUDENT as object( id ), name ), age ) ); / 第二步:创建一个数组类型 (任意选择下面 ...

随机推荐

  1. MSSQL死锁进程查看及关闭

    select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName from sys.dm_tran ...

  2. 网址导航18A

    [导航] hao268 百度导航 泡泡导航 35Q网址导航 [名站] 百度 网易 腾讯 新华 中新 凤凰 [邮箱] 163邮箱 126邮箱 Yeah邮箱 QQ邮箱 阿里邮箱 189邮箱 [新闻] 联合 ...

  3. webpack 打包问题

    Project is running at http://localhost:8080/webpack output is served from /dist/webpack: wait until ...

  4. mysql8.0 1251错误

    ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER; ALTER USER '; FLUSH PR ...

  5. PHP基础(命名错误)错误导致的500

    记得去年七月份参与一个项目发生的一个低级错误 因为不小心在命名一个方法时使用了一list做为名称,因此在后面无明业火被点燃. 发现一个500,无论如何调整总是500,数据库,文件,url都正确,但就是 ...

  6. Scrum冲刺阶段4

    成员今日完成的任务 人员 任务 何承华 学习后端设计 陈宇 后端设计 丁培辉 学习后端设计 温志铭 日程添加界面设计(一半) 杨宇潇 日程添加界面设计(一半) 张主强 服务器构建 成员遇到的问题 人员 ...

  7. linux上安装mysql5.7

    1.下载tar包,这里使用wget从官网下载 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.22-linux-glibc2. ...

  8. 安装 redis 拓展

    PHP API 20121113 PHP Extension 20121212 Zend Extension 220121212 Zend Extension Build API220121212,T ...

  9. Rabbimq 安装过程,还有踩得坑!centos 安装

    一 .安装erlang 1 添加yum 源,在/etc/yum.repos.d 下添加 rabbitmq-erlang.repo,内容如下 # In /etc/yum.repos.d/rabbitmq ...

  10. 洛谷 质因子分 p2043

    #include <iostream>#include <algorithm>#include <cstring>using namespace std; cons ...