## 注入作用域
来自不同语言背景的开发者,在学习Nest时可能预料不到在请求中几乎所有内容都是共享的。我们建立一个连接池到数据库,在全局状态下使用单例服务。 要记住Node.js并不遵循多线程下请求/响应的无状态模式。因此,在我们的应用中使用单例是安全的。
然而,在需要考虑请求生命周期的情况下,存在边缘情况.例如,在GraphQL应用的预请求缓存中,以及请求追踪和多租户条件下,注入作用域提供了一个机制来获取需要的提供者生命周期行为.
### 提供者范围
基本上,每个提供者都可以作为一个单例,被请求范围限定,并切换到瞬态模式。请参见下表,以熟悉它们之间的区别。
|||
|---|----|
| `DEFAULT` | 每个提供者可以跨多个类共享。提供者生命周期严格绑定到应用程序生命周期。一旦应用程序启动,所有提供程序都已实例化。默认情况下使用单例范围。 |
| `REQUEST` | 在请求处理完成后,将为每个传入请求和垃圾收集专门创建提供者的新实例 |
| `TRANSIENT` | 临时提供者不能在提供者之间共享。每当其他提供者向 `Nest` 容器请求特定的临时提供者时,该容器将创建一个新的专用实例 |
> 对于大多数用例,**建议**使用单例范围。请求之间共享提供者可以降低内存消耗,从而提高应用程序的性能(不需要每次实例化类)。
### 使用 (Usage)
为了切换到另一个注入范围,您必须向 `@Injectable()` 装饰器传递一个选项对象。
```typescript
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {}
```
在[自定义提供者](https://docs.nestjs.com/fundamentals/custom-providers)的情况下,您必须设置一个额外的范围属性。
```typescript
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
```
> `Scope`从`@nestjs/common`中导入。
> 网关不应该使用请求范围提供者,因为其必须作为单例提供。每个网关都封装了一个`socket`并且不能多次实例化。
默认使用单例范围,并且不需要声明。如果你想声明一个单例范围的提供者,在`scope`属性中使用`Scope.DEFAULT`值。
### 控制器范围
控制器也可以有范围,它适用于在该控制器中声明的所有请求方法处理程序。与提供者作用域一样,控制器的作用域声明了它的生命周期。对于请求范围的控制器,为每个入站请求创建一个新实例,并在请求完成处理时进行垃圾收集。
`scope`使用对象的属性声明控制器范围`ControllerOptions`:
~~~typescript
@Controller({
path: 'cats',
scope: Scope.REQUEST,
})
export class CatsController {}
~~~
> 网关永远不应该依赖于请求范围的提供者,因为它们充当单例。一个网关封装了一个真正的套接字,不能多次被实例化
### 所有请求注入
必须非常谨慎地使用请求范围的提供者。请记住,`scope` 实际上是在注入链中冒泡的。如果您的控制器依赖于一个请求范围的提供者,这意味着您的控制器实际上也是请求范围。
想象一下下面的链: `CatsController <- CatsService <- CatsRepository `。如果您的 `CatsService` 是请求范围的(从理论上讲,其余的都是单例),那么 `CatsController` 也将成为请求范围的(因为必须将请求范围的实例注入到新创建的控制器中),而 `CatsRepository` 仍然是单例的。因为它依赖于注入的服务。不依赖的`CatsRepository`将保持单例范围。
> 在这种情况下,循环依赖关系将导致非常痛苦的副作用,因此,您当然应该避免创建它们。
### 请求提供者
在 `HTTP` 应用程序中(例如使用`@nestjs/platform-express`或`@nestjs/platform-fastify`),当使用请求范围提供者时,可能需要获取原始的请求对象。您可以通过注入`REQUEST`对象来做到这一点。
```typescript
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`。
```typescript
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` 试图缓存尽可能多的元数据,它仍然必须为每个请求创建类的实例。因此,它将降低您的平均响应时间和总体基准测试结果。如果您的提供者不一定需要请求范围,那么您应该坚持使用单例范围。
- 介绍
- 概述
- 第一步
- 控制器
- 提供者
- 模块
- 中间件
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 自定义装饰器
- 基础知识
- 自定义提供者
- 异步提供者
- 动态模块
- 注入作用域
- 循环依赖
- 模块参考
- 懒加载模块
- 应用上下文
- 生命周期事件
- 跨平台
- 测试
- 技术
- 数据库
- Mongo
- 配置
- 验证
- 缓存
- 序列化
- 版本控制
- 定时任务
- 队列
- 日志
- Cookies
- 事件
- 压缩
- 文件上传
- 流式处理文件
- HTTP模块
- Session(会话)
- MVC
- 性能(Fastify)
- 服务器端事件发送
- 安全
- 认证(Authentication)
- 授权(Authorization)
- 加密和散列
- Helmet
- CORS(跨域请求)
- CSRF保护
- 限速
- GraphQL
- 快速开始
- 解析器(resolvers)
- 变更(Mutations)
- 订阅(Subscriptions)
- 标量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 联合类型
- 枚举(Enums)
- 字段中间件
- 映射类型
- 插件
- 复杂性
- 扩展
- CLI插件
- 生成SDL
- 其他功能
- 联合服务
- 迁移指南
- Websocket
- 网关
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 适配器
- 微服务
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定义传输器
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 独立应用
- Cli
- 概述
- 工作空间
- 库
- 用法
- 脚本
- Openapi
- 介绍
- 类型和参数
- 操作
- 安全
- 映射类型
- 装饰器
- CLI插件
- 其他特性
- 迁移指南
- 秘籍
- CRUD 生成器
- 热重载
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模块
- Swagger
- 健康检查
- CQRS
- 文档
- Prisma
- 静态服务
- Nest Commander
- 问答
- Serverless
- HTTP 适配器
- 全局路由前缀
- 混合应用
- HTTPS 和多服务器
- 请求生命周期
- 常见错误
- 实例
- 迁移指南
- 发现
- 谁在使用Nest?