线上告警炸锅!FastJson 又立功了。。

news2024/11/19 23:15:29

前段时间新增一个特别简单的功能,晚上上线前review代码时想到公司拼搏进取的价值观临时加一行log日志,觉得就一行简单的日志基本上没啥问题,结果刚上完线后一堆报警,赶紧回滚了代码,找到问题删除了添加日志的代码,重新上线完毕。

情景还原

定义了一个 CountryDTO

public class CountryDTO {    private String country;
    public void setCountry(String country) {        this.country = country;    }
    public String getCountry() {        return this.country;    }
    public Boolean isChinaName() {        return this.country.equals("中国");    }}

定义测试类 FastJonTest​​​​​​​

public class FastJonTest {    @Test    public void testSerialize() {        CountryDTO countryDTO = new CountryDTO();        String str = JSON.toJSONString(countryDTO);        System.out.println(str);    }}

运行时报空指针错误:

图片

通过报错信息可以看出来是 序列化的过程中执行了 isChinaName()方法,这时候this.country变量为空, 那么问题来了:

  • 序列化为什么会执行isChinaName()呢?

  • 引申一下,序列化过程中会执行那些方法呢?

源码分析

通过debug观察调用链路的堆栈信息

图片

图片

调用链中的ASMSerializer_1_CountryDTO.write是FastJson使用asm技术动态生成了一个类ASMSerializer_1_CountryDTO,

asm技术其中一项使用场景就是通过到动态生成类用来代替java反射,从而避免重复执行时的反射开销

JavaBeanSerizlier序列化原理

通过下图看出序列化的过程中,主要是调用JavaBeanSerializer类的write()方法。

图片

而JavaBeanSerializer 主要是通过 getObjectWriter()方法获取,通过对getObjectWriter()执行过程的调试,找到比较关键的com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer方法,进而找到 com.alibaba.fastjson.util.TypeUtils#computeGetters​​​​​​​

public static List<FieldInfo> computeGetters(Class<?> clazz, //                    JSONType jsonType, //                     Map<String,String> aliasMap, //                    Map<String,Field> fieldCacheMap, //                 boolean sorted, //               PropertyNamingStrategy propertyNamingStrategy //    ){        //省略部分代码....        Method[] methods = clazz.getMethods();        for(Method method : methods){            //省略部分代码...            if(method.getReturnType().equals(Void.TYPE)){                continue;            }            if(method.getParameterTypes().length != 0){                continue;            }         //省略部分代码...            JSONField annotation = TypeUtils.getAnnotation(method, JSONField.class);            //省略部分代码...            if(annotation != null){                if(!annotation.serialize()){                    continue;                }                if(annotation.name().length() != 0){                    //省略部分代码...                }            }            if(methodName.startsWith("get")){             //省略部分代码...            }            if(methodName.startsWith("is")){             //省略部分代码...            }        }}

从代码中大致分为三种情况:

  • @JSONField(.serialize = false, name = "xxx")注解

  • getXxx() : get开头的方法

  • isXxx():is开头的方法

 
 

序列化流程图

图片

示例代码​​​​​​​

/** * case1: @JSONField(serialize = false) * case2: getXxx()返回值为void * case3: isXxx()返回值不等于布尔类型 * case4: @JSONType(ignores = "xxx") */@JSONType(ignores = "otherName")public class CountryDTO {    private String country;
    public void setCountry(String country) {        this.country = country;    }
    public String getCountry() {        return this.country;    }
    public static void queryCountryList() {        System.out.println("queryCountryList()执行!!");    }
    public Boolean isChinaName() {        System.out.println("isChinaName()执行!!");        return true;    }
    public String getEnglishName() {        System.out.println("getEnglishName()执行!!");        return "lucy";    }
    public String getOtherName() {        System.out.println("getOtherName()执行!!");        return "lucy";    }
    /**     * case1: @JSONField(serialize = false)     */    @JSONField(serialize = false)    public String getEnglishName2() {        System.out.println("getEnglishName2()执行!!");        return "lucy";    }
    /**     * case2: getXxx()返回值为void     */    public void getEnglishName3() {        System.out.println("getEnglishName3()执行!!");    }
    /**     * case3: isXxx()返回值不等于布尔类型     */    public String isChinaName2() {        System.out.println("isChinaName2()执行!!");        return "isChinaName2";    }}

运行结果为:

 
isChinaName()执行!!getEnglishName()执行!!{"chinaName":true,"englishName":"lucy"}

代码规范

可以看出来序列化的规则还是很多的,比如有时需要关注返回值,有时需要关注参数个数,有时需要关注@JSONType注解,有时需要关注@JSONField注解;当一个事物的判别方式有多种的时候,由于团队人员掌握知识点的程度不一样,这个方差很容易导致代码问题,所以尽量有一种推荐方案。这里推荐使用@JSONField(serialize = false)来显式的标注方法不参与序列化,下面是使用推荐方案后的代码,是不是一眼就能看出来哪些方法不需要参与序列化了。

 
public class CountryDTO {    private String country;
    public void setCountry(String country) {        this.country = country;    }
    public String getCountry() {        return this.country;    }
    @JSONField(serialize = false)    public static void queryCountryList() {        System.out.println("queryCountryList()执行!!");    }
    public Boolean isChinaName() {        System.out.println("isChinaName()执行!!");        return true;    }
    public String getEnglishName() {        System.out.println("getEnglishName()执行!!");        return "lucy";    }
    @JSONField(serialize = false)    public String getOtherName() {        System.out.println("getOtherName()执行!!");        return "lucy";    }
    @JSONField(serialize = false)    public String getEnglishName2() {        System.out.println("getEnglishName2()执行!!");        return "lucy";    }
    @JSONField(serialize = false)    public void getEnglishName3() {        System.out.println("getEnglishName3()执行!!");    }
    @JSONField(serialize = false)    public String isChinaName2() {        System.out.println("isChinaName2()执行!!");        return "isChinaName2";    }}

三个频率高的序列化的情况

图片

以上流程基本遵循 发现问题 --> 原理分析 --> 解决问题 --> 升华(编程规范)。

围绕业务上:解决问题 -> 如何选择一种好的额解决方案 -> 好的解决方式如何扩展n个系统应用;

围绕技术上:解决单个问题,顺着单个问题掌握这条线上的原理。

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

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

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

相关文章

3.C++动态内存管理(超全)

目录 1 .C/C 内存分布 2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free 3. C内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 3.3 operator new函数 3.4 定位new表达式(placement-new) &#xff08;了解&#xff09; 4. 常…

iA Writer for Mac:简洁强大的写作软件

在追求高效写作的今天&#xff0c;iA Writer for Mac凭借其简洁而强大的功能&#xff0c;成为了许多作家、记者和学生的首选工具。这款专为Mac用户打造的写作软件&#xff0c;以其独特的设计理念和实用功能&#xff0c;助你轻松打造高质量的文章。 iA Writer for Mac v7.1.2中文…

C语言(操作符)1

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

vue2迁移到vue3,v-model的调整

项目从vue2迁移到vue3&#xff0c;v-model不能再使用了&#xff0c;需要如何调整&#xff1f; 下面只提示变化最小的迁移&#xff0c;不赘述vue2和vue3中的常规写法。 vue2迁移到vue3&#xff0c;往往不想去调整之前的代码&#xff0c;以下就使用改动较小的方案进行调整。 I…

Java并发编程面试问题与答案

1. 什么是线程安全&#xff1f; 答&#xff1a; 线程安全意味着多个线程可以同时访问一个类的实例而不引起任何问题或不一致的结果。线程安全的代码会通过同步机制来确保所有线程都能正确地访问共享资源。 2. 解释Java中的synchronized关键字。 答&#xff1a; synchronized…

Q1营收稳健增长,云从科技如何在“百模大战”的险中求稳?

自从迈入大模型时代&#xff0c;AI行业可谓“一天一个样”。越来越多的企业涌现&#xff0c;舆论热议从未断绝。 但就像所有技术必须经历的那些考验&#xff0c;在现实尺度下&#xff0c;AI顺利走进商业化世界&#xff0c;仍然是少部分玩家掌握的稀缺能力。个中原因不尽相同&a…

解决“未能正确加载QtVsToolPackage包“问题

今天&#xff0c;在使用VS2019Qt插件时&#xff0c;弹出"未能正确加载QtVsToolPackage包"错误&#xff0c;如图(1.1)所示&#xff1a; 图(1.1) 报"未能正确加载QtVsToolsPackage包"错误 出现这种现象的原因是: qt-vsaddin升级失败或者版本不兼容&#xff0…

动手学深度学习3.6 softmax回归的从零开始实现-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;Softmax 回归从零开始实现_哔哩哔哩_bilibili 本节教材地址&#xff1a;3.6. softmax回归的从零开…

物联网实战--平台篇之(二)基础搭建

目录 一、Qt工程创建 二、数据库知识 三、通信协议 四、名词定义 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 一、Qt工程…

GoLang Gin实际使用

所有代码同步到Admin/gitDemo - Gitee.comhttps://gitee.com/mec-deployment-team_0/git-demo/tree/dev/ 1.创建Gin框架 一般设计一个常规的web项目&#xff0c;都需要以下几个模块 runApp 主函数&#xff0c;运行整个项目routes 路由控制&#xff0c;管理跳转以及路由分组co…

kubectl_入门_Pod调整

Pod调度 在默认情况下&#xff0c;一个pod在哪个node节点上运行&#xff0c;是由scheduler组件采用相应的算法计算出来的&#xff0c;这个过程是不受人工控制的。 但是在实际过程中&#xff0c;这并不满足需求&#xff0c;因为很多情况下&#xff0c;我们想控制某些pod到达某…

基于昇腾AI | 英码科技EA500I使用AscendCL实现垃圾分类和视频物体分类应用

现如今&#xff0c;人工智能迅猛发展&#xff0c;AI赋能产业发展的速度正在加快&#xff0c;“AI”的需求蜂拥而来&#xff0c;但AI应用快速落地的过程中仍存在很大的挑战&#xff1a;向下需要适配的硬件&#xff0c;向上需要完善的技术支持&#xff0c;两者缺一不可。 基于此&…

【C语言】深入了解文件:简明指南

&#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&#xff1a; C笔记 &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 文章目录 一、文件的概念1.1 文件名:1.2 程序文件和数据文件 二、数据文…

香港上市现货比特币及以太坊ETF,首日交易量不及预期

原创 | 刘教链 在BTC&#xff08;比特币&#xff09;继续回调至61k一线&#xff0c;眼见就要在连续7个月收涨后&#xff0c;终于迎来一个收跌的4月之际&#xff0c;香港如期上市了现货比特币ETF和以太坊ETF&#xff0c;成为全球继美国推出现货比特币ETF之后&#xff0c;亚洲首个…

Aker(安碁科技)晶振产品应用和选型

一、石英晶体振荡器简介 在电子电路系统中&#xff0c;特定的动作需要严格按照一定的顺序进行&#xff0c;以确保数据被正确处理和操作&#xff0c;时钟信号就成了系统工作的重要引导者。而且在多模块复杂电路系统中&#xff0c;为了确保不同功能模块能协调一致地工作&#xf…

网络安全是智能汽车下一个要卷的方向?

2024年一季度&#xff0c;中国汽车市场延续了2023年的风格&#xff0c;核心就是「卷」。 2023年&#xff0c;我国汽车市场爆发「最强价格战」&#xff0c;燃油车的市场空间不断被挤压&#xff0c;如今只剩下最后一口气。近日乘联会发布4月1-14日最新数据&#xff0c;新能源&am…

ARM功耗管理背景及挑战

安全之安全(security)博客目录导读

使用 scikit-learn 进行机器学习的基本原理-2

介绍 scikit-learn 估计器对象 每个算法都通过“Estimator”对象在 scikit-learn 中公开。 例如&#xff0c;线性回归是&#xff1a;sklearn.linear_model.LinearRegression 估计器参数&#xff1a;估计器的所有参数都可以在实例化时设置&#xff1a; 拟合数据 让我们用 nump…

频谱模拟器

频谱模拟器&#xff0c;特别是模拟频谱仪&#xff0c;是一种基于特定原理的频谱分析工具。以下是对其的详细介绍&#xff1a; 工作原理&#xff1a; 模拟频谱仪的工作原理主要基于频率转换原理&#xff0c;包括两个关键步骤&#xff1a;信号混频和滤波分析。 信号混频&#xf…

el-table-column 表格列自适应宽度的组件封装说明

针对组件业务上的需求&#xff0c;需要给 el-table-column 加上限制&#xff0c;需保证表头在一行展示&#xff0c;部分列的内容要一行展示&#xff0c;自适应单项列的宽度&#xff1b; 1、先计算数据渲染后的 el-table-column 文本宽度&#xff1b; 因列表的有些数据需要做到…