为什么在实体上同时使用@AllArgsConstructor 和@NoArgsConstructor?

问题描述

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

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

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

解决方法

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

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

Product.class.newInstance();

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

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

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

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

@Data 还捆绑了 @ToString,因此您无需再次添加。

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

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

    @Id
    private int id;

    private String name;

    private String type;

}

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

,

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

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

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

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

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

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

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

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

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

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

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