【问题标题】:Why to use @AllArgsConstructor and @NoArgsConstructor together over an Entity?为什么在实体上同时使用@AllArgsConstructor 和@NoArgsConstructor?
【发布时间】:2021-09-19 15:42:55
【问题描述】:

我在 IntelliJ 中的 Spring Boot 应用程序上在线看到了多个代码,许多代码同时使用 @AllArgsConstructor@NoArgsConstructor 并且两者都是构造函数,但是每个的目的是不同的 -

  • @AllArgsConstructor 为注释类中的每个字段生成一个需要参数的构造函数
  • @NoArgsConstructor 生成一个没有参数的构造函数

那我们为什么要在同一个实体上同时使用两者?在这种情况下它们如何发挥作用?

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
public class Product {
    @Id
    private int id;
    private String name;
    private String type;
}

【问题讨论】:

  • 实体中需要一个默认构造函数(没有任何参数的构造函数)。这是必需的,因为 JPA/Hibernate 使用默认构造函数方法来使用反射创建 bean 类。如果您不指定任何构造函数(也没有指定 Lombok 注解),Java 将生成一个默认构造函数(如果没有为该类定义构造函数,则由编译器自动生成)。但是如果你添加一个带参数的构造函数(或@AllArgsConstructor),那么你还需要添加一个无参数构造函数(或@NoArgsConstructor),JPA/Hibernate 才能工作。
  • @Yann39 好的,我明白了。现在,我宣布@AllArgsConstructor。但是为什么 JPA 不能在不声明 @NoArgsConstuctor 的情况下工作呢?
  • 因为只有在没有为类定义构造函数的情况下,Java 才会生成默认构造函数。一旦定义了一个,它就不会生成无参数构造函数。另请注意,您正在使用@Data,它捆绑了@RequiredArgsConstructor 的功能,它将为所有最终或@NonNull 注释字段生成构造函数。因此,由于您只使用非最终可为空的字段,因此即使您不添加 @NoArgsConstructor,它也可能会生成一个空构造函数。不过,我还没有测试过最后一个案例。
  • 好的,现在明白了,谢谢
  • 好。我发表评论是因为我现在没有时间写一个干净的答案,但我可以稍后发布更详细的答案。

标签: spring-boot jpa constructor lombok intellij-plugin


【解决方案1】:

JPA specification 要求所有持久类 (@Entity) 都有一个无参数构造函数,公共的或受保护的。 (请注意,在处理诸如 Hibernate 之类的实现时,这不一定是正确的,请参阅this answer)。

这是必需的,因为 JPA 使用默认构造函数方法使用反射 API 创建 bean 类。实际上,如果您的类包含许多构造函数,那么 JPA 将不知道要调用哪个构造函数,这就是它使用反射通过其无参数构造函数实例化该类的原因:

Product.class.newInstance();

相当于new Product()Product.class 是一个类文字,如果在类路径中找不到该类,它可能会在运行时失败),然后,一旦实例化,使用字段设置器处理它。

然后,在 Java 中,会自动为类生成默认构造函数(无参数构造函数),除非您定义其他构造函数(仅当您不提供任何其他构造函数时才会这样做)。

因此,因为当没有定义其他构造函数时编译器会自动创建一个默认的无参数构造函数,所以如果框架(此处为 JPA)需要,只有定义构造函数的类还必须包含无参数构造函数。这就是为什么添加@AllArgsConstructor注解需要添加@NoArgsConstructor注解的原因。

还请注意,您正在使用@Data,它捆绑了@RequiredArgsConstructor 的功能,它将为所有final@NonNull 注释字段生成一个构造函数(请参阅Lombok documentation)。因此,当您仅使用非最终可为空的字段时,即使您不添加 @NoArgsConstructor 注释,它也可能会生成一个空的构造函数。不过我还没有测试过最后一种情况,我知道直接使用带有非最终可为空字段的@RequiredArgsConstructor 时它会生成一个空构造函数,但我不知道使用@Data 时它是否同样有效。

@Data 还捆绑了@ToString,所以你不需要再次添加它。

如果我不需要所有捆绑的注释,我个人不喜欢使用@Data,所以我通常只使用:

@Entity
@Getter
@Setter
@EqualsAndHashCode
public class Product {

    @Id
    private int id;

    private String name;

    private String type;

}

因为我经常不使用toString() 也不使用参数化构造函数。它可能更冗长但对我来说更有意义。

【讨论】:

    【解决方案2】:

    这些是来自 Lombok 的注释。要了解为什么需要它,您必须了解事情的内部运作方式。

    JPA 说
    它的规范说“JPA 规范要求所有持久类都有一个无参数构造函数。这个构造函数可能是公共的或受保护的。因为当没有定义其他构造函数时编译器会自动创建一个默认的无参数构造函数,只有类定义构造函数的还必须包含一个无参数构造函数。"

    为了进一步理解,当它使用反射创建实体时,它使用 Class.newInstance() 方法,该方法需要 无参数构造函数 来创建实例。

    Spring最常用的依赖注入类型是

    1. 基于构造函数的注入
    2. 基于 Setter 的注入

    基于构造函数的注入:当您通过传递所有参数来创建对象时,您基本上使用了构造函数注入。当我们拥有所有参数值并且我们想要创建一个所有值都已初始化的对象时,应该这样做。(@AllArgsConstructor)

    基于 Setter 的注入:我们首先创建一个对象(不使用 arg-constructor),然后使用 setter 更新依赖项或值。(@NoArgsConstructor)

    构造函数注入和setter注入之间有许多关键区别。

    • 部分依赖: 可以使用 setter 注入,但不能通过构造函数注入。假设a中有3个属性 类,具有 3 个 arg 构造函数和 setter 方法。在这种情况下,如果 您只想传递一个属性的信息,可以通过 仅限 setter 方法。

    • 覆盖: Setter 注入覆盖构造函数注入。如果我们同时使用构造函数和 setter 注入,IOC 容器将 使用 setter 注入。

    • 更改:我们可以通过 setter 注入轻松更改值。它不会像构造函数一样创建新的 bean 实例。所以二传手 注入比构造函数注入灵活。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-23
      • 1970-01-01
      • 2011-02-16
      • 1970-01-01
      • 2016-09-28
      • 2023-03-06
      • 1970-01-01
      相关资源
      最近更新 更多