NestJS之依赖注入

715 阅读3分钟

1-什么是DI

在软件工程中,依赖注入(dependency injection,缩写为 DI)是一种软件设计模式,也是实现控制反转的其中一种技术。这种模式能让一个物件接收它所依赖的其他物件。“依赖”是指接收方所需的对象。“注入”是指将“依赖”传递给接收方的过程。在“注入”之后,接收方才会调用该“依赖”[1]。此模式确保了任何想要使用给定服务的物件不需要知道如何创建这些服务。取而代之的是,连接收方物件(像是 client)也不知道它存在的外部代码(注入器)提供接收方所需的服务。 注:编程语言层次下,“接收方”为对象和 class,“依赖”为变量。在提供服务的角度下,“接收方”为客户端,“依赖”为服务。

总结一下就是:消费者只需要拿来(声明索取)用就行,而不需要关心如何以及何时创建所依赖的实例,以及不需要关心复杂的循环引用关系,降低使用门槛;该设计的目的是为了分离关注点,分离接收方和依赖,从而提供松耦合以及代码重用性。1

2-NestJS是如何实现的依赖管理

以下以Service注入资源管理为例:

  • 第一步:收集Module上Import的所有Module以及SubModule(通过反射装饰器获取信息,参考段落3.2),建立树形依赖关系结构。
  • 第二步:通过树形结构Module先创建Controller原型对象(参考段落3.1),然后创建Controller实例
  • 第三步:获取Controller实例构造函数上的依赖对象(参考段落3.2)
  • 第三步:注入Controller实例所依赖的Service对象实例2

3-补充技术细节

3.1-原型拷贝和创建区别

使用原型拷贝的方式,避免过早初始化构造函数中的内容、构造函数的传入参数,以及类属性的初始化。

// 声明一个类对象
class Sample {
    constructor() {
        console.log('Invoked Sample Ctor Function')
    };
    x = 0;
    y = 0;
}
// 使用原型拷贝-不触发构造函数日志
const a = Object.create(Sample.prototype);  
// 使用New关键字创建-触发类构造函数日志
const b = new Sample();

3.2-反射读取函数的装饰器内容

这里的装饰器实现,是使用typescript的实现,并使用辅助库reflect-metadata实现

@Injectable()
class AppService {
  constructor(private readonly CatService) {}
}
// 通过开启TS转JS:
AppService = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [CatService])
], AppService);
// 最后通过`reflect-metadata`来获取构造函数中的其他依赖
var result = Reflect.getMetadata("design:paramtypes", AppService);

3.3-对引用对象赋值,而不改变对象引用本身

var a = {};
var b = Object.assign(a, {id: 1});
a===b // true

3.4-WeakMap的使用和影响

当无法保证对象一定会在复杂的传递过程中被垃圾回收34,并且改对象和另外一个“引用”对象的生命周期相同时,可以考虑使用WeakMap存储。

3.4.1-WeakMap的Key不会计入引用计数,不会组织垃圾回收(目前)

var wm = new WeakMap()
function Foo() {
  this.arr = new Array(5 * 1024 * 1024)
}
var f = new Foo()
wm.set(f, 'data')
f = null
console.log("---> ",wm.get(f)) // undefined, 如果f没有被置空,则打印'data'[^3]

3.4.2-WeakMap的值的生命周期会跟随Key的生命周期

let key = {};
let value = { inner: 1 };
let vm = new WeakMap();
vm.set(key, value);
value = null;
console.log('before release value: ', value, vm.get(key));
key = null;
console.log('after release value: ', value, vm.get(key));
// before release value:  null {inner: 1}
// after release value:  null undefined

9-参考引用

Footnotes

  1. 依赖注入Wiki

  2. 启动与依赖注入

  3. 理解WeakMap的weak

  4. JavaScript垃圾回收