来啊踩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 ) ); / 第二步:创建一个数组类型 (任意选择下面 ...
随机推荐
- vue--1.环境搭建及创建项目
转自https://blog.csdn.net/junshangshui/article/details/80376489 一.环境搭建及创建项目 1.安装node.js,webpack 2.安装vu ...
- Git merge 不同的branch
Git的优势是可以创建不同的branch,然后在每个branch上开发.那么问题是:如果不同的branch之间需要做同步,比如sourceBranch上做的修改也需要同步到targetBranch,改 ...
- python websocket 客户端连接
# -*- coding: utf-8 -*-import jsonimport websocketimport _thread as thread # try:# import thread# ex ...
- FTP做作业用到的知识点:
FTP做作业用到的知识点: 一: os.path 模块下常用的用法 os.path.abspath(file) #返回的是.py文件的绝对路径(完整路径) os.path.dirname(file) ...
- [DP][NOIP2013]花匠
花匠 问题描述: 花匠栋栋种了一排花,每株花都有自己的高度.花儿越长越大,也越来越挤.栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致. ...
- Python之路(第三十六篇)并发编程:进程、同步异步、阻塞非阻塞
一.理论基础 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都是围绕进程的概念展开的. 即使可以利用的cpu只有一个(早期的 ...
- 用turtle库实现汉诺塔问题~~~~~
汉诺塔问题 问题描述和背景: 汉诺塔是学习"递归"的经典入门案例,该案例来源于真实故事. ...
- sha1withRSA算法
RAS_USE_PRIVATE_ENCRYPT(3021300906052b0e03021a05000414 + SHA1(DATA))
- 使用WinMerge作为git的Merge工具
使用WinMerge作为git的Merge工具 我比较喜欢使用免费的WinMerge作为diff和merge工具,虽然TortoiseGit也自己带了TortoiseGitMerge工具,但是使用起来 ...
- mysql too many connection 解决
最近的项目用了动态切换数据源起初感觉还好,后来发现每次切换数据库都会创建一个新的连接,这样就导致大量的sleep线程.而mysql的默认sleep时间是28800秒....默认最大连接数为151,这就 ...