案例:
package com;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import java.nio.charset.StandardCharsets;
@Data
public class User {
private Long id;
private String name;
private String a;
public byte[] getBytes() {
return a.getBytes(StandardCharsets.UTF_8);
}
public static void main(String[] args) {
User user = new User();
user.setName("");
user.setId(1L);
String a = JSONObject.toJSONString(user);
}
}
问题:
getBytes方法虽然有空指针问题,但是没有被调用到不应该报空指针错误。
原因:
fastjson扫描被序列化的类的所有的属性和getXxx方法,将属性和xxx做为新生成的类的新属性,JSONObject.toJSONString获取所有属性时会调用getBytes来获取bytes属性,getBytes方法本身导致的空指针就会报错。
fastJson源码分析:
JSON#toJSONString
调用com.alibaba.fastjson.JSON#toJSONString(java.lang.Object, com.alibaba.fastjson.serializer.SerializeConfig, com.alibaba.fastjson.serializer.SerializeFilter[], java.lang.String, int, com.alibaba.fastjson.serializer.SerializerFeature...)方法
JSONSerializer#write(java.lang.Object)
SerializeConfig#getObjectWriter(java.lang.Class<?>, boolean)
SerializeConfig#createJavaBeanSerializer(java.lang.Class<?>)
获取原class信息
TypeUtils#buildBeanInfo(java.lang.Class<?>, java.util.Map<java.lang.String,java.lang.String>, com.alibaba.fastjson.PropertyNamingStrategy, boolean)
TypeUtils#computeGetters(java.lang.Class<?>, com.alibaba.fastjson.annotation.JSONType, java.util.Map<java.lang.String,java.lang.String>, java.util.Map<java.lang.String,java.lang.reflect.Field>, boolean, com.alibaba.fastjson.PropertyNamingStrategy)
TypeUtils#computeFields
生成新class
SerializeConfig#createJavaBeanSerializer(com.alibaba.fastjson.serializer.SerializeBeanInfo)
SerializeConfig#createASMSerializer
ASMSerializerFactory#createJavaBeanSerializer
SerializeConfig#createJavaBeanSerializer(com.alibaba.fastjson.serializer.SerializeBeanInfo)
将新class生成string
JSONSerializer#write(java.lang.Object)
JavaBeanSerializer#JavaBeanSerializer(com.alibaba.fastjson.serializer.SerializeBeanInfo)
JavaBeanSerializer#write(com.alibaba.fastjson.serializer.JSONSerializer, java.lang.Object, java.lang.Object, java.lang.reflect.Type, int, boolean)
FieldSerializer#getPropertyValueDirect
FieldInfo#get
思考和总结
-
编写方法时不应该假设某些值一定会存在,在有些不熟悉的代码中可能绕过了你原本的逻辑,在每个方法中都做好防御编程。
-
使用ASM生成新字节码的方式是否有更好的打印堆栈方式,提供给开发者更清晰的原因,方便排查问题。
-
多熟悉自己使用的第三方库,对执行方式有一定的了解。