注:如果追求最简洁的方式,可以直接在需要注入的项目上使用注解
@RequiredArgsConstructor使用此注解只需要引用不可变私有变量,例如:
private final JwtTokenUserInterceptor jwtTokenUserInterceptor;
构造器注入
public class WebMvcConfiguration implements WebMvcConfigurer {
private final JwtTokenUserInterceptor jwtTokenUserInterceptor;
// 使用构造器注入
@Autowired
public WebMvcConfiguration(JwtTokenUserInterceptor jwtTokenUserInterceptor) {
this.jwtTokenUserInterceptor = jwtTokenUserInterceptor;
}
}
setter注入
public class WebMvcConfiguration implements WebMvcConfigurer {
private JwtTokenUserInterceptor jwtTokenUserInterceptor;
// 使用Setter注入
@Autowired
public void setJwtTokenUserInterceptor(JwtTokenUserInterceptor jwtTokenUserInterceptor) {
this.jwtTokenUserInterceptor = jwtTokenUserInterceptor;
}
}
不建议使用字段注入(Field Injection)主要有以下几个原因:
1. 可测试性问题 (Testability):
- 依赖隐藏: 字段注入将依赖关系隐藏在类内部。在单元测试中,你需要使用反射或者其他技巧来设置这些依赖,这使得测试变得更加复杂和脆弱。
- 难以 Mock: 因为依赖是直接注入到字段中,所以很难在测试中 Mock 或者替换这些依赖。 你需要使用 Mocking 框架的反射机制,这使得测试代码更加难以理解和维护。
- 构造函数注入更清晰: 构造函数注入明确地声明了类的依赖关系,使得测试更加容易。你可以直接在构造函数中传入 Mock 对象。
2. 违反了依赖倒置原则 (Dependency Inversion Principle):
- 类与 IoC 容器耦合: 字段注入使得类与 IoC 容器(如 Spring)紧密耦合。 类依赖于 IoC 容器来注入依赖,而不是依赖于抽象。
- 构造函数注入更符合 DIP: 构造函数注入允许你将依赖作为参数传递给构造函数,这使得类更加独立,并且更容易在不同的环境中使用。
3. 不可变性问题 (Immutability):
- 字段注入破坏了类的不可变性: 如果一个类是不可变的,那么它的所有字段都应该在构造函数中初始化,并且不能被修改。 字段注入允许在对象创建之后修改字段,这破坏了类的不可变性。
- 构造函数注入保证不可变性: 构造函数注入可以确保所有依赖都在对象创建时被初始化,并且不能被修改,从而保证了类的不可变性。
4. 隐藏了类的真实依赖:
- 难以理解类的依赖关系: 通过查看类的代码,你很难知道类有哪些依赖。 你需要查看 IoC 容器的配置才能了解类的依赖关系。
- 构造函数注入更清晰: 构造函数注入明确地声明了类的依赖关系,使得代码更容易理解和维护。
5. 潜在的运行时异常:
- NullPointerException 风险: 如果 IoC 容器没有正确地注入依赖,那么字段可能会为
null,导致NullPointerException。 - 构造函数注入可以避免 NPE: 构造函数注入可以确保所有依赖都被正确地注入,从而避免
NullPointerException。
6. 代码可读性问题:
- 代码不够清晰: 字段注入使得代码不够清晰,因为你需要在字段上添加
@Autowired注解,而不是在构造函数中明确地声明依赖。 - 构造函数注入更易读: 构造函数注入使得代码更易读,因为你可以直接在构造函数中看到类的依赖关系。
示例:
字段注入 (不推荐):
@Component
public class MyService {
@Autowired
private MyRepository myRepository;
public void doSomething() {
// 使用 myRepository
myRepository.getData();
}
}
构造函数注入 (推荐):
@Component
public class MyService {
private final MyRepository myRepository;
@Autowired // 可以省略,从 Spring 4.3 开始,如果类只有一个构造函数,@Autowired 可以省略
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
public void doSomething() {
// 使用 myRepository
myRepository.getData();
}
}
总结:
虽然字段注入在代码编写上可能更简洁,但它在可测试性、依赖关系管理、不可变性和代码可读性方面存在诸多问题。 因此,最佳实践是使用构造函数注入,它能够更好地表达类的依赖关系,提高代码的可测试性和可维护性。
其他注入方式:
除了字段注入和构造函数注入,还有 Setter 注入。 Setter 注入通常用于可选依赖,即某些依赖不是必须的。 但是,如果依赖是必须的,仍然建议使用构造函数注入。
总而言之,尽量避免使用字段注入,优先选择构造函数注入。
