推荐一个做照片书的网站,湛江网站建设招聘,wordpress4.2,织梦网站怎么修改内容背景
为了在日志中把出入参打印出来#xff0c;以便验证链路和排查问题#xff0c;在日志中将入参用fastjson格式化成字符串输出#xff0c;结果遇到了NPE。
问题复现
示例代码
public static void main(String[] args) {OrganizationId orgId new OrganizationId();N…背景
为了在日志中把出入参打印出来以便验证链路和排查问题在日志中将入参用fastjson格式化成字符串输出结果遇到了NPE。
问题复现
示例代码
public static void main(String[] args) {OrganizationId orgId new OrganizationId();NodeName name new NodeName(test);Node node new Node();node.setName(name);node.setOrganizationId(orgId);System.out.println(JSONObject.toJSONString(node));
}错误提示 发现是OrganizationId对象里的方法报空指针了赶紧看一眼这个类
public class OrganizationId {private String id;public Long getIdToLong() {return Long.valueOf(this.id);}
}怎么会运行到 getIdToLong 方法呢
问题排查
对 JSONObject.toJSONString 方法进行反复 debug 之后终于发现了原因以下是具体路径
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();}
}往下到 serializer.write 方法 public final void write(Object object) {if (object null) {out.writeNull();return;}Class? clazz object.getClass();ObjectSerializer writer getObjectWriter(clazz);try {writer.write(this, object, null, null, 0);} catch (IOException e) {throw new JSONException(e.getMessage(), e);}}再到 getObjectWriter注意入参create传了true
public ObjectSerializer getObjectWriter(Class? clazz) {return getObjectWriter(clazz, true);
}在 getObjectWriter 的核心具体实现中走到了自定义对象序列化的流程
// ......
if (create) {writer createJavaBeanSerializer(clazz);put(clazz, writer);
}createJavaBeanSerializer 往下到 TypeUtils.buildBeanInfo
public final ObjectSerializer createJavaBeanSerializer(Class? clazz) {SerializeBeanInfo beanInfo TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy, fieldBased);if (beanInfo.fields.length 0 Iterable.class.isAssignableFrom(clazz)) {return MiscCodec.instance;}return createJavaBeanSerializer(beanInfo);
}在 buildBeanInfo 中由于入参 fieldBased 是false会走到 computeGetters 的逻辑
ListFieldInfo fieldInfoList fieldBased? computeGettersWithFieldBase(beanType, aliasMap, false, propertyNamingStrategy) //: computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, false, propertyNamingStrategy);看到 computeGetters 的名字感觉八成是这里了发现里面有一段逻辑是扫描以 get 开头的方法名把方法后缀变成一个属性后续在获取对应属性时会去运行对应的 getter 方法
if(methodName.startsWith(get)){// 省略...// 从方法名中解析出属性名propertyName Character.toLowerCase(methodName.charAt(3)) methodName.substring(4);
}从上面这段代码可以获取到 propertyName 的值为 idToLong并且对应的 fieldInfo 是 getIdToLong 方法。 到这里基本水落石出了原来是fastjson序列化是扫描以 “get”还有“is” 开头的方法并且从该方法名中提取属性如果对应的方法中存在问题那么这里就可能遇到对应的异常就像本文遇到的NPE。
解决方案
1、 业务逻辑中处理保证 node 对象中的 orgId 不为空避免NPE。 2、日志打印中处理不序列化整个对象只打出关键信息避开可能为空的字段。 3、 在调用JSON.toJSONString的时候加上SerializerFeature.IgnoreNonFieldGetter参数忽略掉所有没有对应成员变量Field的getter函数可以正常序列化。
JSONObject.toJSONString(node, SerializerFeature.IgnoreNonFieldGetter)4、 通过在函数上 getXxx() 增加JSONField(serialize false)注解也能达到同样的效果。
JSONField(serialize false)
public Long getIdToLong() {return Long.valueOf(this.id);
}computeGetters 中消费注解的代码
JSONField annotation method.getAnnotation(JSONField.class);// ...if(annotation ! null){if(!annotation.serialize()){continue;}// ...if(methodName.startsWith(get)){
// ... 总结
fastjson 将对象转为 string 时会把以“get”开头的方法认为是属性的 getter把 getXXX 方法后面的 XXX 变成一个属性并通过 getXXX 方法去获取如果get方法内存在异常逻辑就可能报错。可以尽量避免使用JSON打日志。
附录
1、阿里巴巴开发规约
2、默认根据get方法进行序列化根据java bean的定义通过反射来获取javaBean定义见什么是JavaBean、bean?