@RequiredArgsConstructor 和构造同时使用,注入会不会导致空指针
会,极有可能导致空指针异常。 如果你在同一个类上同时使用 @RequiredArgsConstructor 和显式编写的构造方法,并且处理不当,就会发生空指针。
详细解释
- @RequiredArgsConstructor 的作用
Lombok 的 @RequiredArgsConstructor 会在编译时为类生成一个构造方法。这个构造方法会为所有被 @NonNull 注解标记的、未初始化的 final 字段生成一个参数。
java
@Service
@RequiredArgsConstructor
public class MyService {
private final SomeRepository repository; // final 字段,会被包含
private AnotherService anotherService; // 非final,不会被包含// Lombok 会自动生成这个构造方法:
// public MyService(SomeRepository repository) {
// this.repository = repository;
// }
}
当 Spring 容器创建这个 Bean 时,它会自动寻找这个构造方法并使用它来注入 SomeRepository 依赖。这是构造器注入,也是 Spring 官方推荐的方式。
- 当你同时编写构造方法时会发生什么?
Java 的规则是:如果你显式地定义了任何构造方法,编译器就不会再为你生成默认的无参构造方法。
Lombok 也遵循类似的规则:如果你显式定义了任何构造方法,Lombok 的 @RequiredArgsConstructor 就不会生效(除非你使用了 @RequiredArgsConstructor(onConstructor = …) 等特殊配置,但这是另一个话题)。
错误示例:
java
@Service
@RequiredArgsConstructor // 这行注解实际上失效了!
public class MyService {
private final SomeRepository repository;// 程序员自己写的构造方法
public MyService() {System.out.println("MyService created");// 问题:repository 字段没有被赋值!// this.repository = null;
}public void doSomething() {repository.findById(1L); // 这里会抛出 NullPointerException!
}
}
发生了什么?
你添加了 @RequiredArgsConstructor,期望 Lombok 生成 MyService(SomeRepository repository)。
但是,你又显式地编写了一个无参构造方法 MyService()。
Lombok 检测到你已经有了一个构造方法,因此它不会再生成它自己的构造方法。
Spring 在创建 MyService Bean 时,发现没有带参数的构造方法,于是选择了你写的无参构造方法。
通过无参构造方法创建对象后,final 字段 repository 没有被初始化,其值为 null。
当你调用 repository 的任何方法时,自然就会抛出 NullPointerException。
- 如何正确使用?
最佳实践(二选一):
方案一:纯粹依赖 Lombok(推荐)
不要自己写构造方法,完全让 @RequiredArgsConstructor 来负责生成。这是最简洁、最不容易出错的方式。
java
@Service
@RequiredArgsConstructor
public class MyService {
private final SomeRepository repository;
// 其他业务逻辑...
// 不要写任何构造方法!
}
方案二:完全自己管理构造方法(适用于需要额外初始化逻辑时)
如果你需要在构造方法里做一些额外的初始化工作,那就不要使用 @RequiredArgsConstructor,自己完整地编写构造方法,并用 @Autowired 注解(在 Spring 4.3 之后,如果只有一个构造方法,可省略 @Autowired)。
java
@Service
public class MyService {
private final SomeRepository repository;// 自己编写构造方法,并完成注入和初始化
public MyService(SomeRepository repository) {this.repository = repository; // Spring 会从这里注入// ... 这里可以写其他初始化代码System.out.println("MyService created with repository: " + repository);
}
}
总结
场景 结果 建议
只用 @RequiredArgsConstructor 正常。Lombok 生成构造方法,Spring 用它来注入。 推荐
@RequiredArgsConstructor + 自定义无参构造 异常。Lombok 不生成构造方法,Spring 使用无参构造,导致依赖为 null。 禁止
@RequiredArgsConstructor + 自定义带参构造 混乱且危险。行为不可预测,极易出错。 禁止
只用自定义构造方法 正常。清晰明确,可添加自定义逻辑。 可选
结论:永远不要在同一个类上混合使用 @RequiredArgsConstructor 和显式编写的构造方法。 选择其中一种方式并坚持使用。对于大多数只需要注入依赖的场景,直接使用 @RequiredArgsConstructor 是最佳选择。