背景
客服同学反馈使用某功能时,页面提示异常。
像往常一样,小伙伴积极响应,按部就班的先翻日志,找到异常信息,定位到哪一块范围,然后就...(给出反馈结果)
小伙伴看了小一会儿,怎么看都感觉没有问题,正准备赶班车之际,小伙伴喊渣渣勇也看下这段逻辑,大概瞄了两眼确实没发现问题(主要还是太菜了),就先撤了...
以下内容暂不讨论代码编写规范问题...
正文
1、一筹莫展
路上回想那段逻辑,好像确实没什么问题... 坚信勤能补拙的渣渣勇,回来后还是决定翻下代码
翻了下线上异常日志信息:
image.png看到这日志信息,这不是很明显,类名 + 行数 都提示了嘛... 按常规套路,翻出对应代码... (需确保代码跟线上版本一致)
异常 563行 逻辑如下:
image.png这么看,好像这行代码确实没什么问题(其实这里有很大问题!)
后面想线上跟本地打的包不一致导致,down了对应的class文件下来,如红色框所示:
image.png这就很奇怪了,难道真的是代码有毒?
2、峰回路转
事到如今,也只好拔出我的大杀器,40米长的大刀 -- Arthas !
先 trace 一下对应的方法,页面再模拟请求下:
image.pngok,很明显,空指针出来了!
再使用 jad 命令反编译对应方法 (由不得感叹arthas 真香):
image.png看到红色框所示,由不得感叹一声,what the help (Fxxx)?
到这里,原因已经很明显了, 当对象不为空、属性为空时,就会出现 NPE;
应该有小伙伴跟我一样会有疑问,为什么反编译会多出来 Integer.value; OK,其实就是图二中的 DEFAULT 搞得鬼!
private static final int DEFAULT = 0;
因为对象用的是 Integer,涉及到装箱的操作,所以会多出 Integer.value 方法;(这里体现出打的包中的 class 文件多么不靠谱)
3、问题复现
为了验证以上问题,写了个demo来复现一下:
/**
* Author : bingo624
* Date : 2020/9/17 23:23
* Description :
* version : 1.0
*/
@RestController
public class HelloController {
private static final int DEFAULT = 0;
@RequestMapping("/hello1")
public StudentVo hello1(){
PhxUserDto phxUserDto = new PhxUserDto();
StudentVo vo = new StudentVo(phxUserDto == null ? DEFAULT : phxUserDto.getGender());
return vo;
}
@RequestMapping("/hello2")
public StudentVo hello2(){
PhxUserDto phxUserDto = new PhxUserDto();
StudentVo vo = new StudentVo(phxUserDto == null ? null : phxUserDto.getGender());
return vo;
}
@RequestMapping("/hello3")
public StudentVo hello3(){
PhxUserDto phxUserDto = new PhxUserDto();
StudentVo vo = new StudentVo(phxUserDto == null ? Integer.valueOf(0) : phxUserDto.getGender());
return vo;
}
}
结合上述介绍,猜猜看,是否都能正常返回?
聪明如你,不出所料, 请求 /hello1 是会出现 NPE 返回 500
image.png/hello2 和 /hello3 都是能正常响应的!
最后来看看打的jar包中的class文件,以及线上反编译后的类:
jar包中的class文件:
image.png线上反编译后:
image.png4、柳暗花明
翻了下阿里规约:(拆箱装箱都有可能引发 NPE)
image.png官网指引: docs.oracle.com/javase/spec…
image.png总结
通过一个案例,体现出基础知识的重要性;代码编写规范,可以避免很多问题;
犯错误是很正常的,更应该思考的是:出现问题如何通过掌握的各种工具来解决,以及不重复犯错!
最后再安利一波 arthas, 真香!(还有很多高级特性等待你的发掘...)
网友评论