使用装饰器注入

如果每次代码都需要手动绑定,然后通过 get/getAsync 方法拿到对应的对象,那将会非常繁琐,由于 在设计之初 midway/injection 体系就基于 ts,参考了业界的 IoC 实现,完成了属于自己的依赖注入能力,主要是通过 @provide@inject 两个装饰器来完成绑定定义和自动注入属性,大大简化了代码量。

TIP

由于使用了依赖注入体系,我们希望所有的业务代码都通过 class 语法来完成

  1. @provide()
  2. export class UserService {
  3. @inject()
  4. userModel;
  5. async getUser(userId) {
  6. return await this.userModel.get(userId);
  7. }
  8. }

我们可以看到业务代码的样子和以往有着一些不同。

  • 类加了装饰器,同时直接导出,不需要关心如何实例化
  • 属性加了装饰器,但是没有任何初始化以及赋值的操作即可使用

@provide()

有了 @provide() 装饰器,就可以简化绑定,被 IoC 容器自动扫描,并绑定定义到容器上,对应的逻辑是 绑定对象定义

@provide(id?) 的参数为对象 id,可选。

注意

@provide 装饰器是用于自动被 IoC 容器装载。

@inject()

@inject() 的作用是将容器中的定义实例化成一个对象,并且绑定到属性中,这样,在调用的时候就可以访问到该属性。

注意

注入的时机为构造器(new)之后,所以在构造方法(constructor)中是无法获取注入的属性的,如果要获取注入的内容,可以使用 构造器注入

父类的属性使用 @inject() 装饰器装饰,子类实例会得到装饰后的属性。

  1. class Parent {
  2. @inject()
  3. katana1;
  4. }
  5. class Child extends Parent {
  6. @inject()
  7. katana2;
  8. }
  9. class Grandson extends Child {
  10. @inject()
  11. katana3;
  12. }

Grandson 的实例 grandson 拥有 @inject() 装饰器注入的 grandson.katana3, grandson.katana2, grandson.katana1 属性。

实现时,会查找 Gradson 的原型链,遍历原型链上所有用 @inject() 装饰的属性,运行装饰器,注入相应的属性。

查找类的原型使用 reflect-metadata使用装饰器注入 - 图1 仓库的 OrdinaryGetPrototypeOf使用装饰器注入 - 图2 方法,使用 recursiveGetPrototypeOf 方法以数组形式返回该类的所有原型。

  1. function recursiveGetPrototypeOf(target: any): any[] {
  2. const properties = [];
  3. let parent = ordinaryGetPrototypeOf(target);
  4. while (parent !== null) {
  5. properties.push(parent);
  6. parent = ordinaryGetPrototypeOf(parent);
  7. }
  8. return properties;
  9. }