为什么在实体上一起使用 @AllArgsConstructor 和 @NoArgsConstructor ?

NIK*_*ATH 30 constructor jpa intellij-plugin lombok spring-boot

我在网上看到了有关 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;
}
Run Code Online (Sandbox Code Playgroud)

Yan*_*n39 22

JPA规范要求所有持久类 ( @Entity) 都具有无参数构造函数(公共的或受保护的)。(请注意,在处理像 Hibernate 这样的某些实现时,这不一定是真的,请参阅此答案)。

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

Product.class.newInstance();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

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

    @Id
    private int id;

    private String name;

    private String type;

}
Run Code Online (Sandbox Code Playgroud)

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


Tri*_*riS 15

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

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

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

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

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

基于构造函数的注入(@AllArgsConstructor): 当您通过传递所有参数创建对象时,基本上使用构造函数注入。当我们拥有所有参数值并且想要创建一个所有值均已初始化的对象时,应该执行此操作。@AllArgsConstructor生成一个构造函数,需要为带注释的类中的每个字段提供一个参数。

基于 Setter 的注入 (@NoArgsConstructor):我们首先创建一个对象(不使用 arg-constructor),然后使用 setter 更新依赖项或值。@NoArgsConstructor生成一个不带参数的默认构造函数。

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

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

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

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

@NoArgsConstructor将生成一个没有参数的构造函数。如果这是不可能的(由于最终字段),则会导致编译器错误,除非@NoArgsConstructor(force = true)使用 ,否则所有最终字段都用0 / false / null初始化。对于有约束的字段,例如@NonNull字段,不会生成检查,因此请注意,这些约束通常不会满足,直到这些字段稍后正确初始化。某些 java 构造函数(例如 hibernate 和服务提供者接口)需要无参数构造函数。@Data该注释主要与生成注释的其他构造函数之一或其中之一结合使用。

@AllArgsConstructor为类中的每个字段生成一个带有 1 个参数的构造函数。标记为 的字段@NonNull会导致对这些参数进行空检查。

结论:

  • @AllArgsConstructor 生成一个构造函数,需要为带注释的类中的每个字段提供一个参数。
  • @AllArgsContstructor 还允许使用 staticName 属性创建静态工厂方法
  • @NoArgsConstructor 生成一个没有参数的默认构造函数
  • @NoArgsConstructor 可以创建用于构造目的的静态工厂方法
  • Lombok 无法调用超级构造函数,除非它有一个无参数构造函数
  • 如果超类没有无参数构造函数,Lombok 无法在子类中生成任何构造函数