提供者的基本概念
在 NestJs 中有一个提供者的概念,提供者可以是服务、缓存、工厂、数据库连接等。
提供者的主要思想就是它可以作为依赖注入项注入到需要使用的地方,这样我们就可以根据业务需求和自己的想法来组建业务功能组件从而让开发的灵活性大大提高,并且这些提供者的实例我们可以委托为 NestJs 来帮我们运行。
提供者的关系例子如下图(官方例子):
在上一章节中我们创建了一个commodity.controller
控制器,而这个控制器应该负责 HTTP 请求的处理,复杂的业务处理应该交给提供者,其实提供者就是一个普通的 JavaScript 类。
服务
先让我们创建一个简单的CommodityService
服务类,该服务主要是负责数据的添加和查询操作,并且我们首先先把此服务注入到app.module
里面,后续我们再介绍 module 的使用。具体例子如下:
import { Injectable } from "@nestjs/common";
import { Commodity } from "../interfaces/commodity.interface";
@Injectable()
export class CommodityService {
private readonly commoditys: Commodity[] = [];
create(commodity: Commodity) {
this.commoditys.push(commodity);
}
findAll(): Commodity[] {
return this.commoditys;
}
findById(id: string) {
return this.commoditys.find((item: Commodity) => item.id === id);
}
}
// app.module.ts
@Module({
controllers: [AppController, CommodityController],
providers: [AppService, CommodityService],
})
export class AppModule {}
这个CommodityService
类其实就是一个基本类,具有一个属性和三个方法。
我们使用@Injectable()
装饰器来声明元数据,元数据声明就是让Nest IoC
来管理我们的服务类。
在上述的例子中,我们还是定义了一个接口用来声明商品的实例属性,具体实例如下:
export interface Commodity {
id: string;
name: string;
type: string;
price: number;
describe: string;
}
有了提供者后,我们就可以通过类构造函数把 CommodityService
引入到控制器中并进行使用,具体的代码如下:
@Controller("commodity")
export class CommodityController {
constructor(private commodityService: CommodityService) {}
@Post("/save")
saveCommodity(@Body() commodity: Commodity) {
this.commodityService.create(commodity);
}
@Get("/all")
getCommoditys() {
return this.commodityService.findAll();
}
@Get("/:id")
getCommodityById(@Param() params: { id: string }) {
return this.commodityService.findById(params.id);
}
}
依赖注入
在 NestJs 中,借助 TypeScript 的功能,管理依赖项变得非常容易,因为他们仅仅通过类型来解析。
在上述的例子中,我们使用了@Injectable()
装饰器来注入对应的提供者,而要使用的时候我们主要在对应的构造函数中声明就可以得到对应提供者的实例。例如:
constructor(private commodityService: CommodityService) {}
注意:在 NestJs 中提供者和控制都需要在 Module 中声明才能使用,要不然会报错。
范围
提供者通常具有与应用程序生命周期同步的生命周期(“范围”)。当应用程序启动时,必须解决每个依赖项,因此必须实例化每个提供程序。同样,当应用程序关闭时,每个提供程序都将被销毁。但是,也有一些方法可以使您的提供程序生命周期限定在请求范围内,后续我们会详细说明。
定制提供者
Nest 有一个内置的控制反转(“IoC”)容器,可以解析提供者之间的关系。此功能是上述依赖项注入功能的基础,但实际上比我们迄今为止所描述的功能要强大得多。有多种方法可以定义提供程序:您可以使用普通值、类以及异步或同步工厂,在后续的笔记中会有记录。
可选提供者
有时,您可能存在不一定需要解决的依赖关系。例如,您的类可能依赖于配置对象,但如果没有传递任何内容,则应使用默认值。在这种情况下,依赖关系变得可选,因为缺少配置提供程序不会导致错误。
要指示提供程序是可选的,请@Optional()
在构造函数的签名中使用装饰器。
基于属性的注入
我们可以使用构造函数进行注入外我们还可以使用基于属性的注入
方式来注入提供者。
当我们的顶级类依赖于一个或者多个提供者的时候,那么通过从构造函数调用子类来将它们一路向上传递可能会非常乏味。为了避免这种情况,您可以@Inject()
在属性级别使用装饰器。例如:
import { Injectable, Inject } from "@nestjs/common";
@Injectable()
export class HttpService<T> {
@Inject("HTTP_OPTIONS")
private readonly httpClient: T;
}
注意:如果您的类不扩展另一个提供程序,您应该始终更喜欢使用基于构造函数的注入。
手动实例化
到目前为止,我们已经讨论了 Nest 如何自动处理解决依赖关系的大部分细节。在某些情况下,您可能需要跳出内置依赖注入系统并手动检索或实例化提供程序。下面我们引入两个这样的主题。
- 要获取现有实例或动态实例化提供程序
- 要在功能中获取提供程序 bootstrap()
这两个主题在后续会慢慢进行说明。