Spring 有哪几种依赖注入的方式?

Spring 主要提供了三种依赖注入(DI)的方式,每种方式都有其优缺点和适用场景。

Spring 的三种主要依赖注入方式如下:

  1. 构造函数注入 (Constructor Injection) - 【官方推荐】
  2. Setter 方法注入 (Setter Injection)
  3. 字段注入 (Field Injection)

下面我们来详细解析。


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

这是 Spring 团队首选并推荐的方式。依赖通过类的构造函数进行传递和赋值。

示例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserRepository userRepository; // 依赖可以声明为 final,确保不可变

    // 当类只有一个构造函数时,@Autowired 注解可以省略
    // Spring 会自动使用这个构造函数进行注入
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void findUser() {
        userRepository.getUser();
    }
}
优点:
  • 依赖不可变 (Immutability):可以将依赖声明为 final,确保它们在对象创建后不会被修改,增强了程序的健壮性和线程安全。
  • 依赖关系明确:构造函数清晰地暴露了一个类需要哪些依赖才能工作。没有这些依赖,你甚至无法创建这个类的实例。这符合“高内聚”的设计原则。
  • 保证依赖不为 null:对象在被创建时,其依赖必须已经被注入,否则无法完成实例化。这避免了在后续方法中遇到 NullPointerException 的风险。
  • 更好的可测试性:在单元测试中,你可以非常轻松地 new 一个该类的实例,并手动传入 Mock(伪造)的依赖对象,而无需启动整个 Spring 容器。
  • 避免循环依赖:如果出现循环依赖(A 依赖 B,B 又依赖 A),Spring 在启动时会因为无法解决构造函数参数而直接抛出异常,让你能尽早发现设计问题。
缺点:
  • 代码略显冗长:如果依赖项很多,构造函数的参数列表会变得很长,代码看起来不够简洁。(但这通常也暗示着这个类可能违反了“单一职责原则”,需要重构)。

2. Setter 方法注入 (Setter Injection)

通过公开的 setXxx() 方法来注入依赖。这是早期 Spring 版本中非常流行的方式。

示例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserRepository userRepository;

    @Autowired // @Autowired 标记在 setter 方法上
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void findUser() {
        // 在调用此方法前,userRepository 必须已经被注入
        userRepository.getUser();
    }
}
优点:
  • 灵活性高:允许在对象创建后,根据需要重新注入或更改依赖。
  • 适用于可选依赖:对于那些不是必须的依赖,Setter 注入更加合适。如果没有提供依赖,对象依然可以被创建,只是部分功能可能无法使用。
缺点:
  • 无法保证依赖的可用性:对象在创建时其依赖可以是 null。只有在 Setter 方法被调用后,依赖才可用。这可能导致在运行时抛出 NullPointerException
  • 依赖可变:对象的状态可以在其生命周期内被任意改变,这可能会引入不确定性和潜在的 bug。
  • 隐藏依赖关系:不像构造函数那样一目了然地展示所有必需的依赖。

3. 字段注入 (Field Injection)

直接在类的字段(成员变量)上使用 @Autowired 注解。这是最简洁的注入方式。

示例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired // 直接在字段上注入
    private UserRepository userRepository;

    public void findUser() {
        userRepository.getUser();
    }
}
优点:
  • 代码极其简洁:省去了编写构造函数或 Setter 方法的麻烦,是三种方式中最省事的。
缺点:
  • 违反了依赖注入的设计原则:它隐藏了类的依赖关系,使得一个类看起来像是不需要任何外部依赖就能工作。
  • 可测试性极差:在进行单元测试时,你无法在不使用 Spring 容器或复杂的反射工具(如 PowerMock)的情况下,为这个类设置 Mock 依赖。你不能简单地 new UserService() 并传入一个假的 UserRepository
  • 依赖不可变性差:无法使用 final 关键字。
  • 与容器强耦合:这种方式完全依赖于 Spring 容器来填充字段,使得组件在容器之外几乎无法使用。

总结与推荐

方式优点缺点推荐级别
构造函数注入依赖不可变、关系明确、保证非空、易于测试、避免循环依赖代码稍多⭐⭐⭐⭐⭐ (强烈推荐)
Setter 方法注入灵活性高,适用于可选依赖依赖可变,可能为null,隐藏必需依赖⭐⭐☆☆☆ (仅用于可选依赖)
字段注入代码最简洁可测试性差、隐藏依赖、与容器强耦合⭐☆☆☆☆ (强烈不推荐用于业务代码)
最终建议:
  1. 首选构造函数注入:对于所有必需的依赖,始终使用构造函数注入。这是一种更安全、更清晰、更符合设计原则的做法。
  2. 谨慎使用 Setter 注入:只在你需要注入一个可选的、或者需要在运行时动态改变的依赖时,才考虑使用 Setter 注入。
  3. 避免在业务代码中使用字段注入:尽管字段注入在很多教程和老项目中很常见,但由于其严重的缺点(尤其是可测试性差),在Spring 开发实践中强烈建议避免使用它。它可能在一些测试类(如 @SpringBootTest)或非常简单的场景下使用,但在核心业务组件中应被禁止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值