NestJS 注入作用域
来自不同语言背景的开发者,在学习Nest时可能预料不到在请求中几乎所有内容都是共享的。我们建立一个连接池到数据库,在全局状态下使用单例服务。 要记住Node.js并不遵循多线程下请求/响应的无状态模式。因此,在我们的应用中使用单例是安全的。
然而,在需要考虑请求生命周期的情况下,存在边缘情况.例如,在GraphQL应用的预请求缓存中,以及请求追踪和多租户条件下,注入作用域提供了一个机制来获取需要的提供者生命周期行为.
提供者范围
基本上,每个提供者都可以作为一个单例,被请求范围限定,并切换到瞬态模式。请参见下表,以熟悉它们之间的区别。
DEFAULT | 每个提供者可以跨多个类共享。提供者生命周期严格绑定到应用程序生命周期。一旦应用程序启动,所有提供程序都已实例化。默认情况下使用单例范围。 |
REQUEST | 在请求处理完成后,将为每个传入请求和垃圾收集专门创建提供者的新实例 |
TRANSIENT | 临时提供者不能在提供者之间共享。每当其他提供者向 Nest 容器请求特定的临时提供者时,该容器将创建一个新的专用实例 |
使用单例范围始终是推荐的方法。请求之间共享提供者可以降低内存消耗,从而提高应用程序的性能(不需要每次实例化类)。
使用 (Usage)
为了切换到另一个注入范围,您必须向 @Injectable() 装饰器传递一个选项对象。
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {}
在自定义提供者的情况下,您必须设置一个额外的范围属性。
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
Scope从@nestjs/common中导入。
网关不应该使用请求范围提供者,因为其必须作为单例提供。每个网关都封装了一个socket并且不能多次实例化。
默认使用单例范围,并且不需要声明。如果你想声明一个单例范围的提供者,在scope属性中使用Scope.DEFAULT值。
控制器范围
当涉及到控制器时,传递 ControllerOptions 对象
@Controller({
path: 'cats',
scope: Scope.REQUEST,
})
export class CatsController {}
网关永远不应该依赖于请求范围的提供者,因为它们充当单例。一个网关封装了一个真正的套接字,不能多次被实例化
所有请求注入
必须非常谨慎地使用请求范围的提供者。请记住,scope 实际上是在注入链中冒泡的。如果您的控制器依赖于一个请求范围的提供者,这意味着您的控制器实际上也是请求范围。
想象一下下面的链: CatsController <- CatsService <- CatsRepository 。如果您的 CatsService 是请求范围的(从理论上讲,其余的都是单例),那么 CatsController 也将成为请求范围的(因为必须将请求范围的实例注入到新创建的控制器中),而 CatsRepository 仍然是单例的。
在这种情况下,循环依赖关系将导致非常痛苦的副作用,因此,您当然应该避免创建它们
请求提供者
在 HTTP 应用程序中(例如使用@nestjs/platform-express或@nestjs/platform-fastify),当使用请求范围提供者时,可能需要获取原始的请求对象。这通过注入REQUEST对象实现:
import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {
constructor(@Inject(REQUEST) private readonly request: Request) {}
}
由于底层平台和协议不同,该功能与微服务和 GraphQL 应用程序略有不同。在 GraphQL 应用程序中,可以注入 CONTEXT来替代REQUEST。
import { Injectable, Scope, Inject } from '@nestjs/common';
import { CONTEXT } from '@nestjs/graphql';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {
constructor(@Inject(CONTEXT) private readonly context) {}
}
然后,您可以配置您的 context 值(在GraphQLModule中),以包含请求作为其属性。
性能
使用请求范围的提供者将明显影响应用程序性能。即使 Nest 试图缓存尽可能多的元数据,它仍然必须为每个请求创建类的实例。因此,它将降低您的平均响应时间和总体基准测试结果。如果您的提供者不一定需要请求范围,那么您应该坚持使用单例范围。
更多建议: