前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Java♨️POJO中自定义特殊get方法导致JSON序列化问题

Java♨️POJO中自定义特殊get方法导致JSON序列化问题

原创
作者头像
写bug的高哈哈
发布2025-02-05 14:32:10
发布2025-02-05 14:32:10
7900
代码可运行
举报
运行总次数:0
代码可运行

POJO 是 Plain Old Java Object 的缩写,是一种简单的 Java 对象,通常用于表示数据。它的结构简单,不依赖于特定的框架。一个基本的 POJO 示例如下所示:

代码语言:java
复制
public class User {
    private String name;
    private int age;

    // name 的 getter 方法
    public String getName() {
        return name;
    }

    // name 的 setter 方法
    public void setName(String name) {
        this.name = name;
    }

    // age 的 getter 方法
    public int getAge() {
        return age;
    }

    // age 的 setter 方法
    public void setAge(int age) {
        this.age = age;
    }
    
}

现在很多项目流行使用 Lombok 函数库简化 POJO 的编写。以下是使用 Lombok 简化后的 User 类型,使用 @Data 来自动产生 getter、setter 等方法:

代码语言:java
复制
@Data
public class User {
    private String name;
    private int age;
}
自定义特殊的 get 方法

在某些场景下,开发者可能会在 POJO 中添加自定义的 get 方法以提供额外的功能。例如下列代码自定义了一个 getNextSubId 方法来方便取得 下一个 subId。

代码语言:java
复制
@Data
static class Order {
    AtomicInteger subId = new AtomicInteger(0);

    //自定义特殊的 get 方法,方便取得下一个 subId
    public int getNextSubId(){
        return subId.incrementAndGet();
    }
}

注意!这个方法并不符合 POJO 中 get 方法的惯例 (Convention),因为调用 getNextSubId 方法是有副作用 (side effect) 的,它改变了 subId 的值。

会造成 JSON 序列化问题?

POJO 用来表示数据,所以常被做序列化处理。例如:序列化为 JSON 字符串。然而,某些 JSON 序列化函数库,在序列化成 JSON 字符串时,会默认调用 POJO 中所有 get 开头命名的方法,导致自定义的特殊的 get 方法被调用。

例如:fastjson 1.2.83 版本,在序列化时就会有这个行为,示例代码使用 JSON.toJSONString 来序列化刚才定义的 Order POJO,如下所示:

代码语言:java
复制
Order order = new Order();
String json1 = JSON.toJSONString(order);
System.out.println(json1);
// 输出:{"nextSubId":1,"subId":{"value":1}}

String json2 = JSON.toJSONString(order);
System.out.println(json2);
// 输出:{"nextSubId":2,"subId":{"value":2}}

上述代码中可以看到每调用一次 JSON.toJSONStringsubId 就会“被递增”,因为在序列化时 getNextSubId 被调用了。代码中序列化做了两次, subId 也从 0 递增到了 2。

这样的 bug 可能被隐藏得很深,例如:在某些条件下才会做序列化(见以下代码):

代码语言:javascript
代码运行次数:0
复制
if (...) {
   ....
   if (...) {
      log.info("order={}", JSON.toJSONString(order));
   }
} else {
   ....
}

Bug 显示的表象就会是 subId 会神奇地在某些情况下自动递增。

而 Bug 的起因仅仅是一段 log 代码。

小结

看似简单的 POJO 也可能隐藏着潜在的问题。魔鬼往往藏在细节之中,而这些细节会在最意想不到的时候引爆问题。

要避免这类陷阱,还需要回归到软件开发的基本功:

  1. 遵循 POJO 的最佳实践:get 方法不应该有副作用。如果需要添加特殊功能的方法,应该选择不同的命名方式 (不要取名 getXXX),避免被 JSON 序列化库误解为标准的 getter 方法。不仅提高了代码的可读性,也减少了序列化过程中的意外行为。
  2. 不要心存侥幸:如上面的例子所示,即使是看似无害的小改动 (如新增一行 log),也可能引入难以察觉的 bug。在进行任何修改时,都应该仔细考虑其可能的影响,特别是在涉及序列化、并发操作或跨系统互动的场景中。
  3. 重视单元测试:单元测试是发现潜在问题的有效方法。测试案例 (Test Case) 应该尽可能覆盖多个分支条件和边界情况。
  4. 选择合适的函数库:在使用第三方函数库 (如 JSON 序列化) 时,要充分了解其行为特性和潜在的陷阱。定期更新这些函数库以获得 bug 修复和安全修补 (patch) 也很重要。
  5. 代码审查 (Code Review) 和持续学习:创建严格的代码审查流程,可以帮助团队成员互相学习,发现潜在的问题。同时,保持对新技术、最佳实践和常见陷阱的学习。

看似老生常谈,但在软件开发的道路上, 这些看似基础的原则往往是确保系统稳定性的关键。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义特殊的 get 方法
  • 会造成 JSON 序列化问题?
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档