provider
是Nest的基本概念。许多基本的Nest类均可以被视为 provider
– services
, repositories
, factories
, helpers
,等。提供程序的主要思想是它能够注入依赖项。这意味着对象能够彼此建立各类关系,而且“链接”对象实例的功能在很大程度上能够委托给Nest运行时系统。Providers只是带有@Injectable()
装饰器的类typescript
在上一章中,咱们构建了一个简单的CatsController
。控制器应处理HTTP请求,并将更复杂的任务委托给Providers。Providers只是普通的JavaScript类,在@Injectable()
其类声明以前带有装饰器json
因为Nest容许以更面向对象的方式设计和组织依赖项,咱们强烈建议遵循SOLID的原则。设计模式
提示: 设计模式中的SOLID原则,分别是单一原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。前辈们总结出来的,遵循五大原则可使程序解决紧耦合,更加健壮。
让咱们从建立一个简单的CatsService开始.数组
这项服务将负责数据存储和检索( 简而言之:服务负责数据的存入和查询 ),而且是为catscocontroller设计的,所以它很适合被定义为提供者Providersapp
import { Injectable } from '@nestjs/common'; import { Cat } from './interfaces/cat.interface'; @Injectable() export class CatsService { private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); } findAll(): Cat[] { return this.cats; } }
提示: 建立一个服务只须要 nest g service cats
咱们的CatsService是一个具备一个属性和两个方法的基本类。惟一的新特性是它使用@Injectable()装饰器。@Injectable()附加元数据,它告诉Nest这个类是一个Nest Providers。顺便说一下,这个例子还使用了一个Cat接口,它可能看起来像这样:异步
export interface Cat { name: string; age: number; breed: string; }
如今咱们有了一个服务类来检索cats,让咱们在CatsController中使用它async
import { Controller, Get, Post, Body } from '@nestjs/common'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; @Controller('cats') export class CatsController { constructor(private readonly catsService: CatsService) {} @Post() async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { return this.catsService.findAll(); } }
CatsService经过类构造函数注入。注意私有只读语法的使用。这个参数容许咱们在同一个位置当即声明和初始化catsService成员ide
Nest创建在强大的设计模式(一般称为“ 依赖注入”)周围。咱们建议在Angular官方文档中阅读有关此概念的精彩文章。毕竟Nest但是直接借鉴了Angular。函数
在Nest中,因为具备TypeScript功能,管理依赖关系很是容易,由于它们仅按类型进行解析。在下面的示例中,Nest将catsService
经过建立并返回的实例CatsService
(或者在单例的正常状况下,若是已在其余地方请求了现有的实例,则返回现有实例)来解决。此依赖关系已解决,并传递给控制器的构造函数(或分配给指定的属性):this
constructor(private readonly catsService: CatsService) {}
提供程序一般具备与应用程序生命周期同步的生命周期(“做用域”)。 引导应用程序时,必须解决每一个依赖关系,所以必须实例化每一个提供程序。 一样,当应用程序关闭时,每一个提供程序都将被销毁。 可是,也有一些方法可使提供程序的生命周期达到请求范围。 您能够在此处阅读有关这些技术的更多信息。
Nest内置了IOC容器( IOC: 控制反转 )可解决提供程序之间的关系。IOC底层的依赖注入功能,但其实远比咱们到目前为止所描述更增强大。
@Injectable()装饰器只是冰山一角,并非定义Providers的惟一方法。 实际上,您可使用纯文本值,类以及异步或同步工厂。 这里提供更多示例
有时,您可能具备不必定必须解决的依赖关系。 例如,您的类可能依赖于配置对象,可是若是未传递任何配置对象,则应使用默认值。 在这种状况下,依赖项变为可选的,由于缺乏配置提供程序不会致使错误。
要指示Providers是可选的,请在构造函数的签名中使用@Optional()装饰器。
@Injectable() export class HttpService<T> { constructor( @Optional() @Inject('HTTP_OPTIONS') private readonly httpClient: T ) {} }
到目前为止,咱们已使用的技术称为基于构造函数的注入,由于Providers是经过构造方法注入的。
在某些很是特定的状况下,基于属性的注入可能会有用。 例如,若是您的顶级类依赖于一个或多个Providers,那么经过从构造函数中调用子类中的super()来将它们一直传递下去可能会很是繁琐。 为了不这种状况,能够在属性级别使用@Inject()装饰器。
import { Injectable, Inject } from '@nestjs/common'; @Injectable() export class HttpService<T> { @Inject('HTTP_OPTIONS') private readonly httpClient: T; }
若是您的类没有扩展其余的Providers,则应始终使用基于构造函数的注入。
如今咱们已经定义了提供程序(CatsService),而且已经有了该服务的使用者(CatsController),咱们须要在Nest中注册该服务,以便它能够执行注入。 咱们能够经过编辑模块文件(app.module.ts)并将服务添加到@Module()装饰器的providers数组中来实现。
import { Module } from '@nestjs/common'; import { CatsController } from './cats/cats.controller'; import { CatsService } from './cats/cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class AppModule {}
Nest如今将可以解析CatsController
该类的依赖关系。
这是咱们的目录结构如今的外观:( 使用类JSON的方式代表,但愿能看懂 )
{ src: { cats: { dto: { create-cat.dto.ts }, interfaces: { cat.interface.ts }, cats.service.ts, cats.controller.ts }, app.module.ts, main.ts } }