【问题标题】:Is it an anti-pattern to use Jackson's `@JsonCreator` on empty constructors?在空构造函数上使用 Jackson 的`@JsonCreator` 是一种反模式吗?
【发布时间】:2022-01-28 03:50:35
【问题描述】:

我反复看到类似下面的代码,其中空的构造函数被好心的开发人员删除(因为它似乎未使用),然后破坏了 Jackson 序列化(后来被测试捕获):

public class Person {
  private String name;
  private int age;
  
  // appears unused – only used by Jackson
  public Person() {}

  // used in code
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
}

我正在考虑在空构造函数中添加@JsonCreator 注释,以更清楚地表明Jackson 使用构造函数来防止开发人员将其删除:

public class Person {
  private String name;
  private int age;
  
  @JsonCreator // <--------
  public Person() {}

  // used in code
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
}

这是一个好主意还是会产生意想不到的后果(即,与无注释情况相比,语义发生了变化)?

或者@JsonCreator 的这种使用是一种反模式,并且有更好的方法来实现这一点(没有将类转换为记录)?

【问题讨论】:

  • 我建议只使用评论以避免意外的副作用
  • 我根本不认为它是一种反模式。它简洁地记录了空构造函数的用途,但更重要的是,可以轻松配置诸如 IntelliJ 之类的 IDE,以在存在特定注释时停止警告未使用的构造函数(以及方法、字段等)。无论您的测试是否应该发现这一点,在测试有价值之前就发现它。
  • @ControlAltDel cmets 没有成功,这就是我要问的原因。
  • “创建单元/集成测试并强制拉/合并请求运行这些测试”会是更好的方法。
  • 这已经完成了。我问这个问题是因为我更喜欢防止错误而不是在运行测试后发现错误。

标签: java json serialization jackson


【解决方案1】:

阅读更多关于此的文档以及关于问题和其他答案的所有 cmets,我得出的结论是,使用 @JsonCreator 作为默认构造函数确实是一种反模式。

Javadoc(例如from version 2.13)指定使用该注解注解的 ctor 应该至少有一个参数。 (它没有明确说明,但是没有参数的 ctor 或多或少是没用的。)

注意:注释创建者方法(构造函数、工厂方法)时,方法必须是:

  • 单参数构造函数/工厂方法没有参数的 JsonProperty 注释:如果是这样,这就是所谓的“委托创建者”,在这种情况下,杰克逊首先将 JSON 绑定到参数的类型,然后调用创建者。这通常与 JsonValue 结合使用(用于序列化)。
  • 构造函数/工厂方法,其中每个参数都使用 JsonProperty 或 JacksonInject 进行注释,以指示要绑定到的属性名称

因此,对于您的问题,我建议您摆脱默认 ctor,而是正确注释代码中使用的至少一个剩余 ctor。 Jackson 可以使用它,IDE 不会将其标记为未使用。

它还可以帮助您避免未正确或完全初始化对象的变通方法。

在您的示例中,这可能是

public class Person {
  private String name;
  private int age;

  @JsonCreator
  public Person(@JsonProperty("name") String name, @JsonProperty("age") int age) {
    this.name = name;
    this.age = age;
  }

  // other getters / setters / business logic come here

}

【讨论】:

  • 你说服了我@Bohemian ;),但我想就如何解决 OPs 问题提供更多建议。
  • 文档中没有提到@JsonCreator 在空参数列表上的行为实际上是一个很好的观点。
  • 由于不明确初始化 @JsonCreator 带注释的 ctor 中的所有字段是合法的,我认为这种默认 ctor 的行为与普通的行为没有什么不同:只需使用个人二传手。但不会让您实现目标 - 您也可以简单地用 @SuppressWarnings("unused") 注释这样的 ctor 以在 IDE 中获得相同的效果,并且更简洁地说明您对其他开发人员的意图 - 特别是如果您添加人类可读的评论为什么你放置压制。如果你删除它仍然没有编译器警告
【解决方案2】:

这是一种反模式,因为默认构造函数是@JsonCreator 默认值,每个人都知道 - 这是无用的代码。

您不需要阻止开发人员删除它,因为如果他们这样做,您的功能测试将会失败。

【讨论】:

  • 我更喜欢防止错误,而不是在运行测试后发现错误。
  • @soc 但您在推送之前在本地运行测试。
  • 是的,还有.....?
  • @soc 最佳实践让您经常在本地运行构建+测试(通常在几分钟内完成) - 基本上是在每一小块编码工作之后。在不太可能且每个开发人员最多一次的情况下,开发人员删除了默认构造函数,在几分钟内,该更改将导致测试失败。我多次遇到这种情况:依靠测试来测试这种东西效果很好。在那里放一个注解,甚至是一个注释(现在 cmets 是一种反模式),并不能阻止任何人删除构造函数,所以无论如何你需要测试。
  • @Bohemian 如果每个人都遵守“最佳实践”,这个问题根本就不会出现——但是随着许多人在编写代码,就会发生人为错误。 (最好的做法是在删除代码之前彻底检查,并通过测试确保删除是无害的。)
猜你喜欢
  • 2017-08-19
  • 2020-03-26
  • 1970-01-01
  • 2016-09-04
  • 1970-01-01
  • 2019-06-23
  • 2013-04-02
  • 1970-01-01
相关资源
最近更新 更多