Spring 依赖注入(Dependency Injection, DI)

Spring 依赖注入(Dependency Injection, DI)

在 Spring 依赖注入(Dependency Injection, DI) 中,有三种主要的方式:

1. 构造函数注入(Constructor Injection)

2. Setter 方法注入(Setter Injection)

3. 字段注入(Field Injection)

1. 构造函数注入(Constructor Injection) ✅ 推荐方式

原理:通过构造函数为 Bean 传递依赖,在对象创建时就完成依赖注入。

优点:

• 不可变性:依赖是 final 的,保证依赖不会被修改,符合 SOLID 原则(SRP, DI)。

• 强制依赖注入:创建对象时就提供了所有必要的依赖,避免了空指针问题。

• 更方便单元测试:可以通过构造函数直接提供 mock 依赖。

示例:

@Component

public class UserService {

private final UserRepository userRepository;

@Autowired // Spring 4.3+ 可省略

public UserService(UserRepository userRepository) {

this.userRepository = userRepository;

}

public void registerUser(String name) {

userRepository.save(name);

}

}

最佳实践:推荐使用构造函数注入,并且让依赖 final,确保不可变。

2. Setter 方法注入(Setter Injection)

原理:Spring 在创建对象后,通过Setter 方法注入依赖。

优点:

• 适用于可选依赖(非必须的依赖)。

• 可以在运行时动态修改依赖。

缺点:

• 依赖可能是 null,导致 NullPointerException。

• 破坏不可变性(对象在创建后依然可以被修改)。

• 依赖不明确,可能创建出**半初始化(partially initialized)**对象。

示例:

@Component

public class UserService {

private UserRepository userRepository;

@Autowired

public void setUserRepository(UserRepository userRepository) {

this.userRepository = userRepository;

}

}

适用场景:如果某些依赖是可选的,可以使用 Setter 注入,但构造函数注入仍是更好的选择。

3. 字段注入(Field Injection) ❌ 不推荐

原理:Spring 直接在字段上使用 @Autowired,无需构造函数或 Setter 方法。

优点:

• 代码更简洁,省去了构造函数和 Setter 方法。

缺点:

• 破坏封装性,外部无法控制依赖,难以进行单元测试(无法直接 mock 依赖)。

• 依赖是隐藏的,违反 DI 原则,可维护性变差。

• 不能使用 final 关键字,导致对象可以被随意修改。

示例:

@Component

public class UserService {

@Autowired

private UserRepository userRepository;

}

避免使用:字段注入适合临时/实验代码,但在生产环境推荐构造函数注入。

三者对比总结

方式

是否推荐

优点

缺点

适用场景

构造函数注入

✅ 强烈推荐

依赖不可变、强制依赖注入、利于测试

代码略长

推荐默认使用

Setter 方法注入

⚠️ 次选

适用于可选依赖

破坏不可变性、可能为空

依赖可选或需要修改时

字段注入

❌ 不推荐

代码最简洁

破坏封装、难以测试、依赖隐藏

临时测试或实验代码

最佳实践

• 默认使用 构造函数注入,并让依赖 final(保证不可变)。

• Setter 注入仅适用于可选依赖,如日志、配置等。

• 避免使用字段注入,特别是测试驱动开发(TDD)时。

如果你的代码需要可测试性、可维护性和更好的设计,构造函数注入是最佳选择! 🚀

相关数据