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

64jmpszr  于 2023-01-09  发布在  Spring
关注(0)|答案(2)|浏览(484)

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

  • @AllArgsConstructor生成一个构造函数,该构造函数需要带注解类中每个字段的参数
  • @NoArgsConstructor生成不带参数的构造函数
    那么,为什么我们要在同一实体上同时使用这两种方法?在这种情况下,它们如何发挥作用?
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
public class Product {
    @Id
    private int id;
    private String name;
    private String type;
}
8yparm6h

8yparm6h1#

这些是Lombok的注解。要理解为什么需要它,你必须理解事物内部是如何工作的。
JPA表示
它的规范说:"* JPA规范要求所有持久化类都有一个无参数构造函数。这个构造函数可以是公共的,也可以是受保护的。因为编译器会在没有定义其他构造函数时自动创建一个默认的无参数构造函数,所以只有定义构造函数的类才必须包含一个无参数构造函数。"
为了进一步理解,当它使用反射创建实体时,它使用 * Class. newInstance()
方法,该方法需要一个无参数构造函数来创建示例。
Spring最常用的依赖注入类型是
1.基于构造函数的注入
1.基于设定器的注入

    • 基于构造函数的注入(@AllArgsConstructor):当你通过传递所有参数来创建对象时,你基本上使用构造函数注入。当我们有了所有参数值并且我们想创建一个所有值都初始化的对象时,应该这样做。@AllArgsConstructor**生成一个构造函数,该构造函数需要为注解类中的每个字段提供一个参数。
    • 基于Setter的注入(@NoArgsConstructor):我们首先创建一个对象(不使用参数构造函数),然后使用setter更新依赖项或值。@NoArgsConstructor**生成一个没有参数的默认构造函数。

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

      • 部分依赖:**可以使用setter注入,但不能通过构造函数注入。假设一个类中有3个属性,有3个arg构造函数和setters方法。在这种情况下,如果只想传递一个属性的信息,则只能通过setter方法。
      • 覆盖:**Setter注入覆盖构造函数注入。如果我们同时使用构造函数和setter注入,IOC容器将使用setter注入。
      • 修改:**我们可以通过setter注入很容易地修改值。它不像构造函数那样总是创建一个新的bean示例。所以setter注入比构造函数注入更灵活。
  • *@NoArgsConstructor**将生成不带参数的构造函数。如果不可能(因为final字段),则将导致编译器错误,除非使用@NoArgsConstructor(force = true),否则所有final字段都用 * 0/false/null * 初始化。对于具有约束的字段,例如@NonNull字段,不生成校验,所以要知道这些约束通常只有在这些字段被正确初始化之后才能满足。某些Java构造,例如Hibernate和服务提供者接口需要无参数构造函数。此注解主要用于与@Data或生成注解的其他构造函数之一结合使用。
  • *@AllArgsConstructor**为类中的每个字段生成一个带1个参数的构造函数。标记为@NonNull的字段将导致对这些参数进行空值检查。

结论:

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

zkure5ic2#

JPA specification要求所有持久化类(@Entity)都有一个无参数构造函数,无论是public还是protected(注意,在处理Hibernate等实现时,这并不一定是真的,请参见this answer)。
这是必需的,因为JPA使用默认的构造函数方法,通过反射API创建bean类。实际上,如果你的类包含许多构造函数,那么JPA不知道调用哪一个,这就是为什么它使用反射通过它的无参数构造函数示例化类:

Product.class.newInstance();

其等价于new Product()Product.class是 * 类字面量 *,如果在类路径中找不到类,则它可能在运行时失败),然后,一旦示例化,就使用字段设置器来处理它。
然后,在Java中,默认构造函数(无参数构造函数)会自动为类生成,除非您定义了其他构造函数(只有在您没有提供任何其他构造函数时才会这样做)。
因此,由于编译器在没有定义其他构造函数时会自动创建一个默认的无参数构造函数,因此只有定义构造函数的类在框架(这里是JPA)需要时才必须包含无参数构造函数,这就是为什么在添加@AllArgsConstructor注解时需要添加@NoArgsConstructor注解的原因。
还要注意,您使用的是@Data,它捆绑了@RequiredArgsConstructor的特性,@RequiredArgsConstructor将为所有final@NonNull注解字段生成构造函数(参见Lombok documentation)。因此,由于您只使用了非final的可空字段,即使您没有添加@NoArgsConstructor注解,它也可能生成一个空构造函数。不过,我还没有测试最后一种情况。我知道当直接使用@RequiredArgsConstructor和非final的可空字段时,它会生成一个空的构造函数,但是我不知道当使用@Data时,它是否同样工作。
@Data还捆绑了@ToString,因此您不需要再次添加它。
如果我不需要所有捆绑的注解,我个人并不喜欢使用@Data,所以我通常只用途:

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

    @Id
    private int id;

    private String name;

    private String type;

}

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

相关问题