跳转至

模块引用

Nest 提供了ModuleRef类来导航提供器的内部列表,并使用其注入令牌作为查找键来获取对任何提供器的引用。 ModuleRef类还提供了一种方法来动态实例化静态和限定作用域的提供器。 ModuleRef可以通过正常的方式注入到类中:

1
2
3
4
@Injectable()
export class CatsService {
  constructor(private moduleRef: ModuleRef) {}
}
1
2
3
4
5
6
7
@Injectable()
@Dependencies(ModuleRef)
export class CatsService {
  constructor(moduleRef) {
    this.moduleRef = moduleRef;
  }
}

ModuleRef类是从@nestjs/core包导入的。

检索实例

ModuleRef实例(模块引用)有一个get()方法。 这个方法使用注入令牌/类名来获取存在于 当前 模块中(已被实例化)的提供器、控制器或可注入对象(例如,守卫、拦截器等)。

1
2
3
4
5
6
7
8
9
@Injectable()
export class CatsService implements OnModuleInit {
  private service: Service;
  constructor(private moduleRef: ModuleRef) {}

  onModuleInit() {
    this.service = this.moduleRef.get(Service);
  }
}
@Injectable()
@Dependencies(ModuleRef)
export class CatsService {
  constructor(moduleRef) {
    this.moduleRef = moduleRef;
  }

  onModuleInit() {
    this.service = this.moduleRef.get(Service);
  }
}

Warning

get()方法不能检索作用域的提供器(瞬态或请求作用域)。 相反,使用下面描述的技术。 此处学习如何控制作用域.

要从全局上下文中检索提供器(例如,如果提供器已被注入到不同的模块中),将 { strict: false } 选项作为第二个参数传递给 get()

this.moduleRef.get(Service, { strict: false });

解决作用域内的提供器

要动态解析作用域的提供器(瞬态或请求作用域),请使用resolve()方法,并将提供器的注入令牌作为参数传递。

1
2
3
4
5
6
7
8
9
@Injectable()
export class CatsService implements OnModuleInit {
  private transientService: TransientService;
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    this.transientService = await this.moduleRef.resolve(TransientService);
  }
}
@Injectable()
@Dependencies(ModuleRef)
export class CatsService {
  constructor(moduleRef) {
    this.moduleRef = moduleRef;
  }

  async onModuleInit() {
    this.transientService = await this.moduleRef.resolve(TransientService);
  }
}

resolve()方法从它自己的 DI 容器子树 中返回提供器的唯一实例。 每个子树都有一个惟一的上下文 标识符。 因此,如果多次调用该方法并比较实例引用,就会发现它们是不相等的。

@Injectable()
export class CatsService implements OnModuleInit {
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    const transientServices = await Promise.all([
      this.moduleRef.resolve(TransientService),
      this.moduleRef.resolve(TransientService),
    ]);
    console.log(transientServices[0] === transientServices[1]); // false
  }
}
@Injectable()
@Dependencies(ModuleRef)
export class CatsService {
  constructor(moduleRef) {
    this.moduleRef = moduleRef;
  }

  async onModuleInit() {
    const transientServices = await Promise.all([
      this.moduleRef.resolve(TransientService),
      this.moduleRef.resolve(TransientService),
    ]);
    console.log(transientServices[0] === transientServices[1]); // false
  }
}

要跨多个resolve()调用生成单个实例,并确保它们共享生成的 DI 容器子树,可以将上下文标识符传递给resolve()方法。 使用ContextIdFactory类来生成上下文标识符。 该类提供了一个create()方法,该方法返回一个适当的惟一标识符。

@Injectable()
export class CatsService implements OnModuleInit {
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    const contextId = ContextIdFactory.create();
    const transientServices = await Promise.all([
      this.moduleRef.resolve(TransientService, contextId),
      this.moduleRef.resolve(TransientService, contextId),
    ]);
    console.log(transientServices[0] === transientServices[1]); // true
  }
}
@Injectable()
@Dependencies(ModuleRef)
export class CatsService {
  constructor(moduleRef) {
    this.moduleRef = moduleRef;
  }

  async onModuleInit() {
    const contextId = ContextIdFactory.create();
    const transientServices = await Promise.all([
      this.moduleRef.resolve(TransientService, contextId),
      this.moduleRef.resolve(TransientService, contextId),
    ]);
    console.log(transientServices[0] === transientServices[1]); // true
  }
}

ContextIdFactory类是从@nestjs/core包中导入的。

注册请求的提供器

手动生成的上下文标识符(使用ContextIdFactory.create())表示 DI 子树,其中REQUEST提供器是undefined,因为它们没有被 Nest 依赖注入系统实例化和管理。

要为手动创建的 DI 子树注册一个自定义的REQUEST对象,请使用ModuleRef#registerRequestByContextId()方法,如下所示:

const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* YOUR_REQUEST_OBJECT */, contextId);

获得当前子树

有时,您可能希望在 请求 上下文中解析请求作用域的提供器的实例。 假设CatsService是请求作用域的,您希望解析CatsRepository实例,该实例也被标记为请求作用域的提供器。 为了共享相同的 DI 容器子树,你必须获取当前的上下文标识符,而不是生成一个新的上下文标识符(例如,使用ContextIdFactory.create()函数,如上所示)。 要获得当前的上下文标识符,首先使用@Inject()装饰器注入请求对象。

1
2
3
4
5
6
@Injectable()
export class CatsService {
  constructor(
    @Inject(REQUEST) private request: Record<string, unknown>,
  ) {}
}
1
2
3
4
5
6
7
@Injectable()
@Dependencies(REQUEST)
export class CatsService {
  constructor(request) {
    this.request = request;
  }
}

了解关于请求提供器的更多信息此处.

现在,使用ContextIdFactory类的getByRequest()方法创建一个基于请求对象的上下文 id,并将其传递给resolve()调用:

const contextId = ContextIdFactory.getByRequest(this.request);
const catsRepository = await this.moduleRef.resolve(CatsRepository, contextId);

动态实例化自定义类

要动态实例化一个之前没有注册为提供器的类,请使用模块引用的create()方法。

1
2
3
4
5
6
7
8
9
@Injectable()
export class CatsService implements OnModuleInit {
  private catsFactory: CatsFactory;
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    this.catsFactory = await this.moduleRef.create(CatsFactory);
  }
}
@Injectable()
@Dependencies(ModuleRef)
export class CatsService {
  constructor(moduleRef) {
    this.moduleRef = moduleRef;
  }

  async onModuleInit() {
    this.catsFactory = await this.moduleRef.create(CatsFactory);
  }
}

这种技术使您能够在框架容器之外有条件地实例化不同的类。