前面在 IOC Container 一节中已经提到了什么是依赖注入,此处不再过多赘述。
简单来说,依赖注入是一种编程范式 / 思想,在许多编程语言的工程体系中都有所运用。其主要目的是将依赖的准备工作交给外部,当前类只需要关注其自身的主要功能即可。
本节仅简单介绍具体到 JVM / Spring 体系中,依赖注入的三种方式。
Setter 注入#
class SimpleService {
private lateinit var simpleDep:SimpleDep
fun setSimpleDep(simpleDep:SimpleDep) {
this.simpleDep = simpleDep
}
}
即先创建 SimpleService,再调用 setter 方法进行设置依赖。
这种注入方式暴露了 setter,变量是完全可变的,这在有些情况下是难以接受的。
私有变量注入#
对于 Spring 而言,结合@Autowired/@Resource注解通过反射功能对该样板代码进行了简化。
class SimpleService {
@Autowired
private lateinit var simpleDep:SimpleDep
}
这种注入方式的特点是,先创建实例,再反射修改私有变量注入依赖。
优点:这种方式看起来很简单、优雅。
缺点:这使得这个类会强依赖于 IoC / DI 容器。
尽管可以移除private修饰符,以自行赋值,但是这会导致封装、可变等其他问题。
构造参数注入#
构造参数注入即在对象创建时就注入依赖。
class SimpleService(private val simpleDep:SimpleDep)
相比于前两种方式,此种方法同时兼具了封装、不可变两个方面,具备良好的安全性,可以避免循环依赖等问题。
对于 setter 方式,提供了封装性,但是值是可变的。
对于私有变量注入的方式,则是既非封装,值也可变,完全是通过反射进行的变更。Spring 可以进行这样的变更,那么调用者也就同样可以进行反射。
这两者均有可能导致循环依赖的问题。
一些值得参考的原则#
- 在大规模多人合作项目中,应当优先采用构造参数注入的方式
- 在单测场景下,适合采用私有变量注入
- 应尽量避免
循环依赖,如果实在无法避免,可以适度使用setter注入,但仍应该避免使用私有变量注入