一.什么是依赖注入?
类通常需要引用其他类。例如,一个Car类可能需要引用一个Engine类,Car类与Engine就有了依赖关系
类引用其他类的方式有哪些?
- 该类直接创建所需的其他类。
/**
* 1.该类直接创建所需的其他类
* Car和Engine是紧耦合的,不能方便的用Engine的子类去替代实现
* 因此也不方便测试不同的Engine子类
*/
class Car {
private val engine = Engine()
fun start() {
engine.start()
}
}
class Car1 {
private var engine:Engine? = null
fun start() {
engine = Engine()
engine?.start()
}
}
- 通过其他类(如工厂类等context.getSystemService())获取。
/**
* 2.通过其他类(如工厂类等context.getSystemService())获取。
*/
class Car2{
private lateinit var manager: ActivityManager
@RequiresApi(Build.VERSION_CODES.M)
fun getSystemService(context: Context){
manager = context.getSystemService(ActivityManager::class.java)
}
}
- 通过参数的形式(包括直接字段赋值,类似于setter方法)
/**
* 3.通过参数形式注入
* 1.构造函数参数
* 2.普通函数参数
*/
class Car3 constructor(private var engine: Engine){
fun start() {
engine?.start()
}
fun setEngine(engine: Engine){
this.engine = engine
}
}
class Car4{
lateinit var engine: Engine
fun start() {
engine.start()
}
}
/**
* 直接字段赋值,类似于 setter 方法
*/
fun main(args: Array<String>) {
Car4().run {
engine = Engine()
start()
}
}
只有第三种引用方式才是依赖注入,依赖注入是一种实现控制反转的形式;依赖注入主要有两种:基于构造方法;基于setter方法。依赖注入是被动的,注入行为是发生在被注入类(如Car)之外的;依赖注入针对的一般是成员变量。
二.自动依赖注入
-
手动依赖的问题
- 在大型应用中,一个类中经常会存在依赖多个其他类的情况,手动依赖会存在大量样板代码
- 当无法在传递依赖项之前构造依赖项时(例如,在使用惰性初始化或将对象作用域确定为应用程序流时),需要编写并维护一个自定义容器类,来管理生命周期内存中的依赖项。
自动依赖能解决手动依赖的问题,提高效率。自动依赖有两种实现方式:
1运行时基于反射的依赖注入(容易有性能问题)
2编译时自动模仿手动依赖注入使用类引用其他类的方式2在一定程度上可以替代依赖注入,但也会存在以下问题:
1测试更多困难。不得不跟同一个全局的类提供者打交道
2因为在类内直接引用,类外很难知道该类需要什么
3如果还涉及到生命周期的管理,那么将更加困难