⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 Hollis 「Hollis」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的。

关于这个”本次请求是否成功”的字段的定义,我见过很多不同的开发者,定义的方式都不同,尤其是在属性的命名上,有人用 success,有人用 isSuccess 表示。

从语义上面来讲,两种命名方式都可以讲的通,并且也都没有歧义。那么还有什么原则可以参考来让我们做选择呢。

根据JavaBeans Specification规定,如果是普通的参数propertyName,要以以下方式定义其setter/getter:

public <PropertyType> get<PropertyName>();

public void set<PropertyName>(<PropertyType> a);

但是,布尔类型的变量propertyName则是单独定义的:

public boolean is<PropertyName>();

public void set<PropertyName>(boolean m);

success方法的 getter 应该是isSuccess/getSuccess,而isSuccess的getter 应该是isIsSuccess/getIsSuccess。

但是很多人,在使用isSuccess作为属性名的时候,还是会采用isSuccess/getSuccess作为 getter 方法名,尤其是现在的很多 IDE 在默认生成 getter 的时候也是会生成isSuccess。

在一般情况下,其实是没有影响的。但是有一种特殊情况就会有问题,那就是发生序列化的时候可能会导致参数转换异常。

我们先来定义一个 JavaBean:

class Model implements Serializable {


private static final long serialVersionUID = 1836697963736227954L;

private boolean isSuccess;

public boolean isSuccess() {

return isSuccess;

}

public void setSuccess(boolean success) {

isSuccess = success;

}

public String getHollis(){

return "yudaoyuanma";

}

}

在这个 JavaBean 中,有一个成员变量isSuccess,三个方法,分别是IDE帮我们自动生成的isSuccess和setSuccess,另外一个是作者自己增加的一个符合getter命名规范的方法。

我们分别使用不同的 JSON 序列化工具来对这个类的对象进行序列化和反序列化:

public class BooleanMainTest {

public static void main(String[] args) throws IOException {

//定一个Model类型

Model model = new Model();

model.setSuccess(true);



//使用fastjson(1.2.16)序列化model成字符串并输出

System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model));



//使用Gson(2.8.5)序列化model成字符串并输出

Gson gson =new Gson();

System.out.println("Serializable Result With Gson :" +gson.toJson(model));



//使用jackson(2.9.7)序列化model成字符串并输出

ObjectMapper om = new ObjectMapper();

System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model));

}

}

以上代码输出结果:

Serializable Result With fastjson :{"yudao":"yudaoyuanma","success":true}

Serializable Result With Gson :{"isSuccess":true}

Serializable Result With jackson :{"success":true,"yudao":"yudaoyuanma"}

在fastjson和jackson的结果中,原来类中的isSuccess字段被序列化成success,并且其中还包含yudao值。而Gson中只有isSuccess字段。

我们可以得出结论:fastjson和jackson在把对象序列化成json字符串的时候,是通过反射遍历出该类中的所有getter方法,得到getHollis和isSuccess,然后根据JavaBeans规则,他会认为这是两个属性yudao和success的值。直接序列化成json:

{“yudao”:”yudaoyuanma”,”success”:true}

但是Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成json:

{“isSuccess”:true}

可以看到,由于不同的序列化工具,在进行序列化的时候使用到的策略是不一样的,所以,对于同一个类的同一个对象的序列化结果可能是不同的。那么,如果我们把一个对象使用fastjson进行序列化,再使用Gson反序列化会发生什么呢?

public class BooleanMainTest {

public static void main(String[] args) throws IOException {

Model model = new Model();

model.setSuccess(true);

Gson gson =new Gson();

System.out.println(gson.fromJson(JSON.toJSONString(model),Model.class));

}

}

以上代码,输出结果:

Model[isSuccess=false]

这和我们预期的结果完全相反,原因是因为JSON框架通过扫描所有的getter后发现有一个isSuccess方法,然后根据JavaBeans的规范,解析出变量名为success,把model对象序列化城字符串后内容为{"success":true}。

根据{"success":true}这个json串,Gson框架在通过解析后,通过反射寻找Model类中的success属性,但是Model类中只有isSuccess属性,所以,最终反序列化后的Model类的对象中,isSuccess则会使用默认值false。

但是,一旦以上代码发生在生产环境,这绝对是一个致命的问题。

所以,作为开发者,我们应该想办法尽量避免这种问题的发生。

所以,**建议大家使用success而不是 isSuccess 这种形式。**这样,该类里面的成员变量时success,getter方法是isSuccess,这是完全符合JavaBeans规范的。无论哪种序列化框架,执行结果都一样。就从源头避免了这个问题。

文章目录