Spring框架(特别是其核心容器)对依赖注入(DI)提供了全面的支持,允许以多种方式将依赖项注入到组件中。依赖注入可以通过构造函数、设定器(setter)方法或直接在字段上完成。尽管Spring支持这三种注入方式,但它推荐使用构造器注入或者设定器注入,而通常不建议使用基于字段的注入。
字段注入的例子
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void registerUser(String username) {
userRepository.save(username);
}
}
主要原因包括:
-
测试难度:
使用构造器或设定器注入,可以在不启动Spring容器的情况下更容易地进行单元测试。你可以通过直接调用构造器或设定器方法来创建对象实例,并注入mock(模拟对象)依赖。而对于字段注入,由于字段通常被声明为私有,你可能需要使用反射API来设置依赖,这会使测试更加复杂和繁琐。 -
不可变性:
通过构造器注入可以创建不可变对象,因为所有必需的依赖项都可以通过构造函数一次性提供,并且相关字段可以被标记为final
,这样有助于确保线程安全性。字段注入通常不能支持这种模式,因为Spring是在对象实例化后,通过反射设置字段值。 -
依赖清晰性:
使用构造器注入,类的依赖关系在API级别变得非常清晰。任何使用该类的开发者都可以通过查看公共构造函数清楚地了解需要哪些依赖。相反,字段注入隐藏了这些依赖关系,使得在不查看类的内部实现的情况下,很难得知该类所依赖的组件。 -
Spring容器的耦合:
字段注入通常依赖于Spring的@Autowired
注解,这增加了组件到Spring框架的耦合。虽然通过设定器和构造器也可能使用这些注解,但是这些方法至少允许在没有Spring的情况下通过其他方式注入依赖。 -
初始化顺序问题:
在某些复杂的依赖关系中,字段注入有可能导致由于初始化顺序错误而出现问题。构造器注入强制所有依赖项在构建对象时即被正确初始化,避免了这种问题。
鉴于上述原因,Spring文档和大多数现代Spring应用推荐尽可能使用构造器注入来保证代码的健壮性,易测试性和清晰性。设定器注入适用于可选依赖和动态依赖,且依然保持了较高的灵活性与可测试性。而字段注入则通常保留给那些简单应用场景,或者因遗留代码改动成本过高等特殊情况。