前端学 IOC(5)- 构造函数注入和属性注入,循环依赖

90 阅读2分钟

什么是构造函数注入,什么是属性注入?

构造函数注入就是:提前构造好依赖,然后传入构造函数的参数中 构造函数注入特点之一是: 可能构造函数中会用到这个依赖,因此必须先把依赖构造好 例如 A 依赖 B : A => B

class A {
  constructor(@Inject('B') b) {
    console.log('构造函数注入, b:', b)
  }
}

// 构造函数注入意味着 在构造 A 之前必须先把 B 构造好
const b = new B()
const a = new A(b)

属性注入的意思是,可以先构造好对象和对象所需的依赖,然后再设置进去 还是举 A 依赖 B 的例子: A => B

class A {
  // 这是伪代码,通过 PropInject 这个装饰器来区分(属性注入)
  @PropInject('B') b
  constructor() {}
}

// 通过上述的 PropInject 装饰器,IOC 框架可以知道 A 依赖 B,并且是属性注入
const a = new A() // 因为是属性注入,可以先构造 A,此时得知 还依赖一个B
// 因而可以继续构造 B ,然后设置给 a

const b = new B() //
a.b = b

什么是循环依赖?

今天我们来讨论,如何避免循环依赖 循环依赖就是,假设 有 A B C 三个类,依赖关系如下: A => B => C => A 那么,在构造 A 的时候,就会 先构造 B , 就会先构造 C,就会先构造 A ... 无限循环...卡死了

有什么办法呢?

第一种办法: 提前判断,如果有循环依赖,报错并提示用户

// 检测依赖种有环...
报错:
错误: 找到了循环依赖:A -> B -> C -> A

第二种办法: request scope 或者 singleton + 属性注入

举个例子, A 依赖 B , B 又依赖 A A => B => A, 本来是会无限循环的 但是,如果双方都是属性注入,+ request scope(一次解析依赖图的过程中有缓存) 或者 singleton(单例)模式下,就不会报错 背后的逻辑类似于


const container = new Container()
container.bind('A').to(A).toRequestScope() // 绑定并设置 A 为 request scope
container.bind('B').to(B).toRequestScope() // 绑定并设置 B 为 request scope

const a = container.resolve('A') // 根据依赖图得知,需要 B
const b = container.resolve('B') // 此时会构造一个B,同时根据依赖图得知,还需要一个A,又因为 a 已经有缓存了,就不在构造A了,直接用刚刚构造的
a.b = b // 互相设置为依赖
b.a = a // 互相设置为依赖