前面有提过,注入 Provider 的方式只需要在 constructor
设计参数并附上对应的型别,或使用 @Inject
装饰器来取得对应的实例,以 app.controller.ts
为例,在 constructor
中直接填入参数并附上型别为 AppService
就可以取得 AppService
的实例:
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
事实上,Nest 还有提供另一种不同的方式来取得内部 Provider 的实例,它叫 模组参照 (Module Reference)。
它是一个名叫 ModuleRef
的 class
,可以对内部 Provider 做一些存取,可以说是该 Module 的 Provider 管理器。
使用上与 Provider 注入的方式相同,在 constructor
注入即可,以 app.controller.ts
为例:
import { Controller } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
@Controller()
export class AppController {
constructor(
private readonly moduleRef: ModuleRef
) {}
}
注入 ModuleRef
以後,可以透过 get
方法来取得 当前 Module 下的 任何元件,如:Controller、Service、Guard 等。
注意:此方法无法在非预设作用域的配置下使用。
这里以 app.controller.ts
为例,我们透过 ModuleRef
来取得 AppService
的实例:
import { Controller, Get } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AppService } from './app.service';
@Controller()
export class AppController {
private readonly appService: AppService;
constructor(
private readonly moduleRef: ModuleRef
) {
this.appService = this.moduleRef.get(AppService);
}
@Get()
getHello() {
return this.appService.getHello();
}
}
透过浏览器查看 http://localhost:3000 是能够正常存取的,表示该方法是正常运作的:
如果要取得 全域 的实例,需要给定参数 strict
为 false
即可取得全域范围的实例,这里先产生一个 StorageModule
与 StorageService
,并将该 Module 设置为全域:
$ nest generate module common/storage
$ nest generate service common/storage
修改 storage.module.ts
的内容:
import { Global, Module } from '@nestjs/common';
import { StorageService } from './storage.service';
@Global()
@Module({
providers: [
StorageService
],
exports: [
StorageService
]
})
export class StorageModule {}
修改 storage.service.ts
的内容:
import { Injectable } from '@nestjs/common';
@Injectable()
export class StorageService {
private list: any[] = [];
public addData(data: any): void {
this.list.push(data);
}
public getList(): any[] {
return this.list;
}
}
接着,调整 app.controller.ts
的内容,透过 ModuleRef
来取得全域实例 - StorageService
:
import { Controller, Get } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { StorageService } from './common/storage/storage.service';
@Controller()
export class AppController {
private readonly storageService: StorageService;
constructor(
private readonly moduleRef: ModuleRef
) {
this.storageService = this.moduleRef.get(StorageService, { strict: false });
this.storageService.addData({ name: 'HAO' });
}
@Get()
getHello() {
return this.storageService.getList();
}
}
透过浏览器查看 http://localhost:3000 能够正常存取,表示该方法是正常运作的:
既然在非预设作用域的配置下无法使用 get
来取得实例,那该如何处理呢?这时候可以透过 resolve
来解决,resolve
会从自身的 依赖注入容器子树 (DI container sub-tree) 返回实例,而每个子树都有一个独一无二的 识别码 (Context Identifier),因此每次 resolve
都会是 不同的实例。
来做个简单的实验,先将 AppService
转化成请求作用域:
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
接着,我们在 AppController
中使用两次 resolve
并比对他们是否为相同的实例,启动 Nest 之後,会在终端机看到比对结果为 false
:
import { Controller, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AppService } from './app.service';
@Controller()
export class AppController implements OnModuleInit {
constructor(
private readonly moduleRef: ModuleRef
) {}
async onModuleInit() {
const [instance1, instance2] = await Promise.all([
this.moduleRef.resolve(AppService),
this.moduleRef.resolve(AppService)
]);
console.log(instance1 === instance2); // false
}
}
如果要将多个 resolve
回传相同的实例,可以透过指定识别码让它们使用相同的子树,进而取得相同的实例。在指定识别码之前,可以透过 ContextIdFactory
这个 class
的 create
方法来产生识别码。这里同样以 AppController
为例,在 resolve
之前先产生识别码并带入 resolve
中,启动 Nest 之後,会在终端机看到结果为 true
:
import { Controller, OnModuleInit } from '@nestjs/common';
import { ContextIdFactory, ModuleRef } from '@nestjs/core';
import { AppService } from './app.service';
@Controller()
export class AppController implements OnModuleInit {
constructor(
private readonly moduleRef: ModuleRef
) {}
async onModuleInit() {
const identifier = ContextIdFactory.create();
const [instance1, instance2] = await Promise.all([
this.moduleRef.resolve(AppService, identifier),
this.moduleRef.resolve(AppService, identifier)
]);
console.log(instance1 === instance2); // true
}
}
透过手动产生的识别码并不会配置到 Nest 的依赖注入系统中,因此,你可以透过 ModuleRef
的 registerRequestByContextId
方法将手动产生的识别码与注入的请求物件做绑定,後续可以透过 ContextIdFactory
的 getByRequest
将识别码从请求物件中取出,进而达到共享子树的效果。
我们这里做个实验,在 AppService
建构时,就产生一个识别码并绑定到注入的请求物件中:
import { Inject, Injectable, Scope } from '@nestjs/common';
import { ContextIdFactory, ModuleRef, REQUEST } from '@nestjs/core';
import { Request } from 'express';
@Injectable({ scope: Scope.REQUEST })
export class AppService {
constructor(
@Inject(REQUEST) private readonly request: Request,
private readonly moduleRef: ModuleRef
) {
const identifier = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(this.request, identifier);
}
}
在 AppController
中也注入 REQUEST
并透过 getByRequest
取得识别码,根据该识别码来执行两次 resolve
以及比对实例:
import { Controller, Get, Inject } from '@nestjs/common';
import { ContextIdFactory, ModuleRef, REQUEST } from '@nestjs/core';
import { Request } from 'express';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(
private readonly moduleRef: ModuleRef,
@Inject(REQUEST) private readonly request: Request
) {}
@Get()
async getTruth() {
const identifier = ContextIdFactory.getByRequest(this.request);
const [instance1, instance2] = await Promise.all([
this.moduleRef.resolve(AppService, identifier),
this.moduleRef.resolve(AppService, identifier)
]);
return instance1 === instance2;
}
}
透过浏览器查看 http//:localhost:3000 会得到结果为 true
。
这里附上今天的懒人包:
ModuleRef
可以对内部 Provider 做一些存取。ModuleRef
预设只能取得当前模组下的实例,透过调整 strict
才能取得全域实例。resolve
来取得实例。resolve
回传的实例都不同,需要透过指定识别码来配置成相同实例。ModuleRef
来绑定识别码。ContextIdFactory
可以产生识别码与从请求物件中取得识别码。进阶功能的部分到这边告一段落,下一篇开始将会进入到 多元化功能 单元,敬请期待!
<<: [13th][Day19] http request header(上)
前言 这个系列主要会介绍一些Web Application攻击手法和一些Real World的案例以...
前情提要 昨天 [day-6] 大致介绍了,电脑的起源与相关发展史,相信各位读资讯或是商业领域的人应...
Nested Interrupts Cortex-M3 和 NVIC 在硬体架构上支援(Nested...
什麽是好的网站设计? 使用者使用网站时是否容易操作及有良好的动线,避免过多不必要的元素,让使用者快速...
搭配 in 的 for 回圈会搜寻物件中所有 property 为 enumerable 者 而使...