目标效果
某个类(TestService)中依赖的其他类(UnitService),不需要在TestService的构造函数中显式的实例化。
只需要使用TS的语法,用private、public、readonly、protected声明构造函数的入参,TestService能自动实例化入参中涉及的类。
被private、public、readonly、protected声明的构造函数参数,会被TS自动放入到类的属性上,和单独声明等效。参考👇问题。
bookmark
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export class UnitService { public sayHello() { console.log('hello world!') } }
@Injectable() export class TestService { constructor( private readonly unitService: UnitService ) { }
public test() { this.unitService.sayHello() } }
|
👆代码中,在TestService的test方法里面,就能通过this访问到UnitService的实例unitService。
实现原理
- 使用Reflect Metadata 获取类的构造函数的入参类型(参考TS元数据)
- 将入参逐个实例化,并且将实例化后的对象放在类中
实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import "reflect-metadata";
const Injectable = () => { return (target: ClassConstructorType) => {} };
const DiFactory = (target: ClassConstructorType) => { const providers = Reflect.getMetadata('design:paramtypes', target) const args = providers.map((provider: ClassConstructorType) => new provider()); return new target(...args) }
|
使用DiFactory来创建TestService,效果如下:
1 2
| const testService = DiFactory(TestService) as TestService testService.test()
|
NestJS中的Injectable实现
在@nestjs@7.0.0
版本中,Injectable() 函数实现和上面的实现一样:
这里还存储了options元信息,去掉defineMetadata的逻辑,就是一个裸的类装饰器。
元信息是怎么注入的?
这里涉及到元编程的概念。
在编译成es5代码的时候,要先处理成语法树。而处理成语法树的过程中,就能拿到参数的具体类型。然后将其保存下来,提供接口支持用户读取即可。
参考链接中的知乎文章中,有翻译后的es5代码。其中,DemoService和上面的TestService一样,InjectService和上面的UnitService一样(被注入对象)。可以看到,翻译后的es5代码,已经有了参数的类型信息。(2019年)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var DemoService = (function() { function DemoService(injectService) { this.injectService = injectService; } DemoService.prototype.test = function() { console.log(this.injectService.a); }; DemoService = __decorate( [Injectable(), __metadata('design:paramtypes', [InjectService])], DemoService ); return DemoService; })();
|
在ts新版本中(2021年),由于装饰器提案一直在推进,翻译后的es5代码已经看不到显式的参数类型了。
1 2 3 4 5 6 7 8 9 10 11 12
| var TestService = (function () { function TestService(unitService) { this.unitService = unitService; } TestService.prototype.test = function () { this.unitService.sayHello(); }; TestService = __decorate([ Injectable ], TestService); return TestService; }());
|
参考链接
bookmark