前言
业务项目使用到 Nestjs 作为Nodejs开发框架,Nestjs 明显区别于 Eggjs 基于约定基于配置的组织方式,采用了依赖注入的方式管理模块。那 Nestjs 是如何实现依赖注入的呢?本文章将对此进行解析。
Nestjs中的依赖注入
@Controller('cats')
export class CatsController1 {
constructor(private catsService: CatsService) {}
}
在这个例子中, 可以看到 CatsController 依赖了 CatsService,然后在执行时 Nestjs 运行时会将 CatsService 的实例传递给 CatsController。
@Controller('cats')
export class CatsController2 {
constructor(private catsService: CatsService, private dogsService:DogsService) {}
}
在这个例子中,CatsController 依赖了 CatsService 和 DogsService,在执行时 Nestjs依然会准确将对应的实例传递给 CatsController。即使 CatsService 和 DogsService 的参数位置对调,实例传递依然是正确的。
一个疑问
Nestjs是如何知道某个参数所对应的类?虽然我们写的代码是TS,但在实际Nodejs运行时,运行的是JS代码,而JS在运行时是没有类型的,也就是说理论上Nestjs运行时并不知道参数对应的类型,但它仍然做对了,是不是很神奇?那接下来我将对这个问题进行解答,而这个问题的奥秘就在于装饰器。
装饰器
装饰器在Class中支持以下几种模式:
- Class
- Class属性
- Class方法
- Class getter/setter
- class方法参数
Nestjs运用了上述大部分装饰器模式,那装饰器在依赖注入的过程中起到了什么样的作用呢?我们来看一下经过转换后的代码。
var CatsController1 = /** @class */ (function () {
function CatsController1(catsService) {
this.catsService = catsService;
}
CatsController1 = __decorate([
(0, common_1.Controller)('cats'),
__metadata("design:paramtypes", [CatsService]) ],
CatsController1);
return CatsController1;
}());
exports.CatsController1 = CatsController1;
var CatsController2 = /** @class */ (function () {
function CatsController2(catsService, dogsService) {
this.catsService = catsService;
this.dogsService = dogsService;
}
CatsController2 = __decorate([
(0, common_1.Controller)('cats'),
__metadata("design:paramtypes", [ CatsService, DogsService]) ],CatsController2);
return CatsController2
;}());
exports.CatsController2 = CatsController2;
可以看到这其中有一个特殊的字符串 design:paramtypes
,那这个字符串代表了什么呢?
元数据
这里就涉及到元数据相关的知识,具体可参考规范[规范](rbuckton.github.io/reflect-met… 作为预定义的设计时类型注释,它分为以下三种类型:
- design:type 装饰目标的类型
- design:paramtypes 装饰目标的参数类型,会严格一一对应参数位置
- design:returntype 装饰目标的返回类型
基于以上三种类型,可以通过 ReflectMetadata 的 getMetadata 方法准确获取到目标的参数类型。
结尾
到这里,想必聪明的你可以猜到,Nestjs 正是利用了这个特性实现了上述问题的解决方案。也由于这个方案的需求,Nestjs强依赖于 typescript 的编译器的 emitDecoratorMetadata 特性。本文主要讲解了 Nestjs 如何实现精准的注入依赖,那 Nestjs 运行时是如何进行组织、管理呢?下篇将详细解析 Nestjs 初始化全流程。