fastjson解析自定义get方法导致空指针问题

背景

为了在日志中把出入参打印出来,以便验证链路和排查问题,在日志中将入参用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 的逻辑:

List<FieldInfo> 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?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1113832.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Java语言实现五子棋

目录 内容 题目 解题 代码 实现 内容 题目 五子棋 使用二维数组,实现五子棋功能. 1.使用二维数组存储五子棋棋盘 如下图 2.在控制台通过Scanner输入黑白棋坐标(例如:1,2 2,1格式 表示二维数组坐标),使用实心五角星和空心五角星表示黑白棋子. 如下图: 输入后重新输出…

基于CNN-GRU-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 CNN&#xff08;卷积神经网络&#xff09;部分 4.2 GRU&#xff08;门控循环单元&#xff09;部分 4.3 Attention机制部分 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版…

使用JDBC操作数据库(IDEA编译器)

目录 JDBC的本质 ​ JDBC好处 JDBC操作MySQL数据库 1.创建工程导入驱动jar包 2.编写测试代码 ​相关问题 JDBC的本质 官方(sun公司) 定义的一套操作所有关系型数据库的规则&#xff0c;即接口各个数据库厂商去实现这套接口&#xff0c;提供数据库驱动jar包我们可以使用这…

Linux中安装Nginx及日常配置使用

高性能的http服务器/反向代理服务器。官方测试支持5万并发&#xff0c;CPU、内存等消耗较低且运行稳定 使用场景 Http服务器。 Nginx可以单独提供Http服务&#xff0c;做为静态网页的服务器。虚拟主机。 可以在一台服务器虚拟出多个网站。反向代理与负载均衡。 Nginx做反向代理…

opencv的图像上下、左右和对角线翻转—flip函数

在OpenCV中&#xff0c;flip函数用于翻转图像。你可以沿x轴、y轴或两者同时翻转图像。这个函数非常直接&#xff0c;可以用于创建镜像图像或旋转图像。 void flip(InputArray src, OutputArray dst, int flipCode);src&#xff1a;输入图像。 dst&#xff1a;翻转后的输出图像…

每日shell脚本之超级整合程序3.0

每日shell脚本之超级整合程序3.0 本期带来之前的升级版2.0整合脚本程序,学习工作小利器,同时模块化构建方便二次开发。 上图 上源码 #!/usr/bin/bash # *******************************************# # * CDDN : M乔木 # # * qq邮箱 …

Microsoft的PromptBench可以做啥?

目录 PromptBench简介 PromptBench的快速模型性能评估 PromptBench数据集介绍 PromptBench模型介绍 PromptBench模型加载遇到的问题 第一次在M1 Mac上加载模型 vicuna和llama系列模型 PromptBench各个模型加载情况总结 PromptBench的Prompt快速工程 chain of thought…

数据驱动决策:掌握高效数据分析的七大步骤

在这个数据驱动的时代&#xff0c;无论是企业决策还是个人发展&#xff0c;数据分析都扮演着至关重要的角色。然而&#xff0c;想要从海量数据中提炼出有价值的信息&#xff0c;并不是一件容易的事情。本文为你详细解读高效数据分析&#xff0c;让你的数据开口说话&#xff0c;…

史上集中度最高的市场!13图读懂“七姐妹”主宰下的美股

随着AI需求大爆发&#xff0c;人类进入人工智能时代。历史上&#xff0c;每当出现新的技术都会经济结构性变化&#xff0c;有的发展得快而平稳&#xff0c;有的则导致了投资的繁荣和萧条周期。此次AI的爆发带来的究竟是泡沫还是繁荣&#xff1f; 德意志银行分析师Jim Reid上周…

【软件架构】03-架构设计流程

1、识别复杂度 确定了系统面临的主要复杂度问题&#xff0c;进而明确了设计方案的目标&#xff0c; 2、设计备选方案 一般三到五个&#xff0c;防止思维狭隘 从需求、团队、技术、资源等综合情况出发&#xff0c;对主流、成熟的架构模式进行选择。 3、评估和选择备选方案 …

UE5 视角跳转

1.在所需要的位置放置 camera actor 2.编写视角跳转蓝图 3.注意

简述全光谱太阳光模拟器的工作原理

太阳是地球上最重要的能源来源之一&#xff0c;它的热能和光能支撑着地球上的所有生命。太阳的能量是如何被科学家们研究和利用的呢&#xff1f;这就要提到太阳光模拟器了。太阳光模拟器是一种能够模拟太阳光的设备&#xff0c;它可以产生与太阳光相似的光谱和强度&#xff0c;…

判断CGH40010F氮化镓管子的好坏-QSWL

判断CGH40010F氮化镓管子的好坏-QSWL 参考&#xff1a;https://zhuanlan.zhihu.com/p/624299054 今天非常搞笑&#xff0c;淘宝买了几个CGH40010F的管子&#xff0c;结果到货的非常奇葩&#xff0c;所有的管子栅极和漏极居然是反的&#xff0c;要商家退款商家还说都是全新原装…

JDBC简介

JDBC体系结构 Java Data Base Connectivity&#xff08;JDBC&#xff09;&#xff0c;Java数据库连接。 JDBC重要的类和接口 JDBC API 定义了一组用于与数据库进行通信的接口和类&#xff0c;这些接口和类都定义在Java.sql包中。 类或接口作用DriverManager处理驱动程序的加…

Internet Download Manager(IDM)中文破解版v6.42.3.1绿色版

IDM软件的多线程下载技术很牛逼&#xff0c;默认16个线程&#xff0c;可以跑满下载速度 提升你的下载速度最多达5倍&#xff0c;安排下载时程&#xff0c;或续传一半的软件。Internet Download Manager的续传功能可以恢复因为断线、网络问题、计算机当机甚至无预警的停电导致下…

【linux】【Shell】Linux基础命令集

目录 一、常用操作命令 二、编辑命令 三、挂载磁盘命令 一、常用操作命令 1、cd :更改文件目录命令 1.切换到主目录cd2.切换到目录/tmpcd /tmp3.切换到当前目录 dir目录cd dir4.切换到根目录cd /5.切换到上一级目录cd ..6.切换到二级目录cd ../..7.切换到主目录&#xff0…

xxl-job架构原理讲解

1、调度中心 调度中心是一个单独的Web服务&#xff0c;主要是用来触发定时任务的执行 它提供了一些页面操作&#xff0c;我们可以很方便地去管理这些定时任务的触发逻辑 调度中心依赖数据库&#xff0c;所以数据都是存在数据库中的 调度中心也支持集群模式&#xff0c;但是…

Pormise---如何解决javascript中回调的信任问题?【详解】

本人编程小白一枚&#xff0c;希望多多包涵~ 如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 本人会很热心的阐述自己的想法&#xff01;谢谢&#xff01;&#xff01;&#xff01; 文章目录 回调中的信任问题回调给我们带来的烦恼&#xff1f;调用过早…

camunda源代码编译运行(二):构建并运行camunda源代码工程

接上一篇文章&#xff1a;camunda源代码编译运行&#xff08;一&#xff09;&#xff1a;下载编译camunda源代码 Camunda 7.19源代码一共有178个maven工程和1个angular前端工程&#xff0c;这么多工程中包括了大量的QA测试包、JDK不同版本适配&#xff08;比如&#xff1a;Jav…

Nginx配置组成与性能调优

目录 一、Nginx配置介绍 1. 模块组成 2. 图示 3. 相关框架 二. 配置调优 1. 全局配置 1.1 关闭版本和修改版本 1.2 修改启动的进程数 1.3 cpu与work进程绑定 1.4 pid路径 1.5 nginx进程的优先级&#xff08;work进程的优先级&#xff09; 1.6 调试work进程打开的文…