NestJS:依赖注入和提供者注册

Posted

技术标签:

【中文标题】NestJS:依赖注入和提供者注册【英文标题】:NestJS: Dependency Injection and Provider Registration 【发布时间】:2021-04-23 00:24:26 【问题描述】:

谁能帮我理解 DI Nest Fundamentals,我的问题:

“是否有可能有一个服务类没有@Injectable 注释,而且这个类不属于任何模块?”我在网上看到一个例子如下:

这个类存在于一个公共文件夹中:

export class NotificationService 
  constructor(
    @Inject(Logger) private readonly logger: LoggerService,
    private readonly appConfigService: AppConfigService,
    @Inject(HttpService) private readonly httpService: HttpService
  ) 
 
  async sendNotification(msg: string) 
   ....
   

然后在 providers 数组的另一个模块中注册:

import  Module, Logger, forwardRef, HttpModule  from '@nestjs/common';
import  MongooseModule  from '@nestjs/mongoose';
import  NotificationService  from '../../commons/notification/notification.service';
 
@Module(
    imports: [
        ...
    ],
    controllers: [InvoiceController],
    providers: [
        InvoiceService,
        NotificationService,
        Logger],
    exports: [InvoiceService]
)
export class InvoiceModule  

然后注入到其他服务的构造方法中

@Injectable()
export class InvoiceService 
 
    constructor(
        @Inject(Logger) private readonly logger: LoggerService,
        private readonly notificationService: NotificationService)  
 
...

这很好用,但我不知道为什么。为什么通知服务在没有添加@Injectable和没有导入模块的情况下正确注入?

【问题讨论】:

【参考方案1】:

那么让我们来分析一下@Injectable() 装饰器到底发生了什么。

通常,我们使用装饰器来设置关于我们正在装饰的类、参数、方法或属性的元数据,或者我们使用它通过描述符以某种方式修改方法(如果方法装饰器)或属性(属性装饰器) .在@Injectable() 的情况下,我们并没有真正做这些。当然我们是setting the scope metadata,但这似乎并没有真正设置任何关于“嘿,这个类可以通过 Nest 框架注入”的元数据。那是因为@Injectable()真正 设置我们的是tsconfigtsc 编译器的特殊属性,emitDecoratorMetadata 属性。有了这个属性,typescript 会在文件的开头和结尾添加一堆额外的函数。这些函数一般看起来是这样的

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) 
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
;
var __metadata = (this && this.__metadata) || function (k, v) 
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
;

DelegatorService = __decorate([
    common_1.Injectable(),
    __metadata("design:paramtypes", [http_interceptor_service_1.HttpInterceptorService,
        websocket_interceptor_service_1.WebsocketInterceptorService,
        rpc_interceptor_service_1.RpcInterceptorService,
        gql_interceptor_service_1.GqlInterceptorService])
], DelegatorService);

这是超级重要的部分,因为"design:paramtypes" 实际上是 Nest 在确定注入什么时正在读取的内容。

这个元数据只有在类中的任何地方使用装饰器时才可用,was actually an eslint-typescript discussion 关于元数据以及import type 如何破坏它,以及真正深入杂草的a recent PR。

我提出所有这些是为了说因为您在构造函数中有@Inject(),所以@Injectable() 实际上是无关紧要的,除非您要设置范围级别的元数据,否则没有必要。类型元数据已经被发出,这就解释了为什么你不需要@Injectable()(尽管我仍然认为拥有它是个好主意,因为它提供了明确的意图)。

现在为什么注入工作正常,我保证这个不那么复杂:您将 NotificationsService 添加到 InvoiceModule's providersarray. This tells Nest that any service inside ofInvoiceModulehas access toNotificationsService` 以便可以注入在这里没有问题。

【讨论】:

感谢您的解释,所以我推断像 NotificationService 这样的类可以存在而不属于任何导出它的模块,其次,在这种情况下使用 register provider , Nest在后台新建一个NotificationService实例,不需要useValue:new NotificationService

以上是关于NestJS:依赖注入和提供者注册的主要内容,如果未能解决你的问题,请参考以下文章

Nestjs 依赖注入 - 将服务注入服务

Nestjs 依赖注入和 DDD / 清洁架构

nestjs 单元测试 createTestingModule 依赖注入

使用 Prisma 2 和 NestJS 进行日志记录 - 依赖注入问题?

在 NestJS 中使用依赖注入导入 TypeScript 模块

Nestjs 应用程序中`@ntegral/nestjs-sentry` 包的依赖注入问题