源码面前,了无秘密

《阿里开发规范泰山版》(2020.04.22)-->编程规约-->(一) 命名风格-->第8条规定:

【强制】POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

对于这样一条【强制】级别的规定,虽然规范中做了简单的说明,但依然显得很不起眼,以至于我虽然规范背的很熟,依然踩到了这个坑。

0 起因

最近写了一个钉钉告警工具类,对于这种需求明确,开发文档清晰的任务,写代码信手拈来,很快就写完了。

但是自测的时候却发现@所有人这个接口始终过不了单元测试,经过一番追踪,终于找到原因,于是有了本文。

1 问题重现

本身发钉钉消息就是一个post请求的事,而且钉钉有阿里背书,调用不通那自然是我这个开发者的问题喽。

下面是钉钉的接口说明,参数isAtAll表示是否@所有人

{
"msgtype": "text",
"text": {
"content": "我就是我, 是不一样的烟火@156xxxx8827"
},
"at": {
"atMobiles": [
"156xxxx8827",
"189xxxx8325"
],
"isAtAll": false
}
}

根据这个json串,我编写了如下的POJO类与之对应,并使用fastjson进行json序列化

public class Message {
/**
* 消息类型
**/
private String msgtype = "text";
/**
* 消息内容对象
**/
private Text text;
/**
* 被@对象
**/
private At at; public static class At{
/**
* 被@人电话
**/
private List<String> atMobiles; /**
* 是否@所有人
**/
private boolean isAtAll;
// 省略getter、setter
}
// 省略无关代码...
}

问题就出在private boolean isAtAll;这个字段上,有没有发现阿里的这个参数违背了开篇提到的开发规范?使用fastjson序列化之后,该属性实际转化为了:

"atAll": true

这个坑爹货,把前面的is给吃了!导致无法@所有人

2 追根溯源

为什么序列化之后把is吃了呢?带着这个问题我追踪了一下fastjson的源码,发现在序列化的时候,其使用的是属性的getter方法名,而isAtAll字段自动生成的getter、setter为:

public boolean isAtAll() {
return isAtAll;
} public void setAtAll(boolean atAll) {
isAtAll = atAll;
}

对应的fastjson中对方法名的处理在com.alibaba.fastjson.util.TypeUtils.computeGetters中,源码摘录如下:

if(methodName.startsWith("is")){
if(methodName.length() < 3){
continue;
}
if(method.getReturnType() != Boolean.TYPE
&& method.getReturnType() != Boolean.class){
continue;
}
String propertyName;
Field field = null;
if(Character.isUpperCase(c2)){
if(compatibleWithJavaBean){
propertyName = decapitalize(methodName.substring(2));
} else{
propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
}
propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName, 2);
}

也就是说,对于is开头的方法,fastjson先把第3个字符截取出来,如果该字符是大写,就转换为小写,并且拼装剩余的方法名组成属性名。

例如:isAtAll方法拼装出来的属性名就是atAll,最终结果就是把我们的is

给吃了!

3 解决办法

既然问题根源已经找到了,那我们只需要对症下药就可以了,这里针对不同应用场景,课代表给大家总结三种解决办法:

  1. 对于有修改权限的代码,要严格遵守开发规范,POJO类中的布尔类型属性不要用is开头命名,如果有,改掉
  2. 对于第三方接口,参数里有类似isXXX这样的参数,可以在对应属性字段上使用fastjson的@JSONField(name = "anotherName")来定制属性名
  3. 可以手动修改getter和setter方法名

4 引申

SpringBoot集成了jackson,默认使用jackson来进行json序列化,经过测试,jackson也存在吃掉is的情况,原理与fastjson类似,就不做过多解释了,开发过程中遵守文中提及的解决办法即可

参考

  • fastjson源码
  • 《阿里开发规范泰山版》(2020.04.22)

欢迎扫码关注我的个人公众号,获取最新文章↓

POJO类中布尔类型为啥不让用isXxx命名的更多相关文章

  1. POJO类中的任何布尔类型的变量,都不要加is

    POJO类中的任何布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误. 定义为基本数据类型boolean isSuccess:的属性,它的方法也是isSuccess(),HSF框架在反向解 ...

  2. 《挑战30天C++入门极限》新手入门:C++中布尔类型

        新手入门:C++中布尔类型 布尔类型对象可以被赋予文字值true或者false,所对应的关系就是真与假的概念. 我们通常使用的方法是利用他来判断条件的真与假,例如下面的代码: #include ...

  3. 为什么阿里巴巴开发手册中强制要求 POJO 类使用包装类型?NPE问题防范

    封面:学校内的秋天 背景:写这个的原因,也是我这两天凑巧看到的,虽然我一直有 alibaba Java 开发手册,也看过不少次,但是一直没有注意过这个问题 属于那种看过,但又没完全看过 一起来看看吧冲 ...

  4. 【Java学习笔记之八】JavaBean中布尔类型使用注意事项

    JavaBean是一个标准,遵循标准的Bean是一个带有属性和getters/setters方法的Java类. JavaBean的定义很简单,但是还有有一些地方需要注意,例如Bean中含有boolea ...

  5. Java bean中布尔类型使用注意

    JavaBean是一个标准,遵循标准的Bean是一个带有属性和getters/setters方法的Java类. JavaBean的定义很简单,但是还有有一些地方需要注意,例如Bean中含有boolea ...

  6. Python中布尔类型

    我们已经了解了Python支持布尔类型的数据,布尔类型只有True和False两种值,但是布尔类型有以下几种运算:与运算:只有两个布尔值都为 True 时,计算结果才为 True.True and T ...

  7. Gson解析POJO类中的泛型参数

    在开发Android与API交互的时候,使用Json格式传输,遇到了这样一个情况,返回数据格式POJO类如下: public class ApiResult<T> { private in ...

  8. go中布尔类型bool的用法

    示例 // bool布尔类型的用法 package main import ( "fmt" "unsafe" ) func main() { // bool类型 ...

  9. vba中布尔类型、字符串操作

    在vba中,bool类型数据可以参与数学运算,true为-1,false为0 Sub mysub() Dim mybool As Boolean Debug.Print Debug.Print myb ...

随机推荐

  1. springmvc使用<mvc:default-servlet-handler/>导致的handler失效

    使用springmvc时,会在web.xml中配置对所有请求进行拦截 <!-- 配置springmvc拦截的请求--> <servlet-mapping> <servle ...

  2. Redis学习笔记(十八) 集群(下)

    复制和故障转移 Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制 的主节点下线时,代替下线主节点继续处理命令请求. ...

  3. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  4. Linux 和 Vim 常用命令整理

    Sftp常用命令: lcd f:本地切换到 F盘 lpwd本地 当前目录 lls本地 文件列表 put 本地 上传文件到服务器(put输入后,回车会有弹窗,选择上传文件) get下载文件到本地 Lin ...

  5. 恕我直言你可能真的不会java第1篇:lambda表达式会用了么?

    本文配套教学视频:B站观看地址 在本号之前写过的一些文章中,笔者使用了lambda表达式语法,一些读者反映说代码看不懂.本以为java 13都已经出了,java 8中最重要特性lambda表达式大家应 ...

  6. vc6.0转vs2012的一些错误与解决方法

    1>------ 已启动生成: 项目: NMW210, 配置: Debug Win32 ------ abs_position = fabs((float)posiTemp1 - (float) ...

  7. Python3-socketserver模块-网络服务器框架

    Python3中的socketserver模块简化了编写网络服务器的任务 在实际的开发中,特别是多并发的情况下,socket模块显然对我们的用处不大,因为如果你要通过socket模块来实现并发的soc ...

  8. Python3-paramiko模块-基于SSH的远程连接模块

    Python3中的paramiko模块,基于SSH用于连接远程服务器并执行相关操作 http://docs.paramiko.org/en/2.1/ SSHClient 用于连接远程服务器并执行基本命 ...

  9. 入门大数据---Flume 简介及基本使用

    一.Flume简介 Apache Flume 是一个分布式,高可用的数据收集系统.它可以从不同的数据源收集数据,经过聚合后发送到存储系统中,通常用于日志数据的收集.Flume 分为 NG 和 OG ( ...

  10. web安全之跨站脚本漏洞(XSS)

    XSS(跨站脚本)概述以及pikachu上的实验操作 Cross-Site Scripting 简称为“CSS”,为避免与前端叠成样式表的缩写"CSS"冲突,故又称XSS. XSS ...