譯自:https://docs.nestjs.com/providers。
Providers是Nest中的一個基本概念。許多基本的嵌套類可以被視為Providers——services、repositories、factories、helpers等等。Providers的主要思想是它可以注入依賴項;這意味著對象可以彼此創(chuàng)建各種關(guān)系,并且對象的“連接”實(shí)例的功能在很大程度上可以委托給Nest運(yùn)行時系統(tǒng)。Providers只是一個用@Injectable()裝飾器注釋的類。

在前一章中,我們構(gòu)建了一個簡單的CatsController??刂破鲬?yīng)該處理HTTP請求,并將更復(fù)雜的任務(wù)委托給Providers。提供者是純JavaScript類,在類聲明之前有一個@Injectable()裝飾器。
提示
由于Nest支持以一種更面向?qū)ο蟮姆绞皆O(shè)計和組織依賴關(guān)系,因此我們強(qiáng)烈建議遵循SOLID原則。
Services
讓我們從創(chuàng)建一個簡單的CatsService開始。該服務(wù)將負(fù)責(zé)數(shù)據(jù)存儲和檢索,并被設(shè)計為供CatsController使用,因此它是一個很好的候選Providers。因此,我們使用@Injectable()裝飾類。
// cats.service.ts
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;
}
}
提示
要使用CLI創(chuàng)建服務(wù),只需執(zhí)行$ nest g service cats命令。
我們的CatsService是一個基本類,有一個屬性和兩個方法。惟一的新特性是它使用@Injectable()裝飾器。@Injectable()裝飾器附加了元數(shù)據(jù),它告訴Nest這個類是一個Nest provider。順便說一下,這個例子也使用了一個Cat接口,它可能看起來像這樣:
export interface Cat {
name: string;
age: number;
breed: string;
}
現(xiàn)在我們有一個服務(wù)類來檢索cats,讓我們在CatsController中使用它:
// cats.controller.ts
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是通過類構(gòu)造函數(shù)注入的。注意使用private readonly語法。這種簡寫允許我們在同一位置立即聲明和初始化catsService成員。
Dependency injection
Nest是圍繞強(qiáng)設(shè)計模式(通常稱為依賴項注入)構(gòu)建的。我們建議閱讀在官方Angular文檔中關(guān)于這個概念的一篇很棒的文章。
在Nest中,由于TypeScript的功能,管理依賴項非常容易,因為依賴項僅通過類型解析。在下面的示例中,Nest將通過創(chuàng)建和返回catsService實(shí)例來解析catsService(或者,在單例的正常情況下,如果已經(jīng)在其他地方請求了現(xiàn)有實(shí)例,則返回它)。該依賴項被解析并傳遞給控制器的構(gòu)造函數(shù)(或分配給指定的屬性):
constructor(private readonly catsService: CatsService) {}
Scopes
Providers 通常有一個與應(yīng)用程序生命周期同步的生命周期(“范圍”)。當(dāng)應(yīng)用程序引導(dǎo)時,必須解析每個依賴項,因此必須實(shí)例化每個provider 。類似地,當(dāng)應(yīng)用程序關(guān)閉時,每個provider 都會被銷毀。但是,也有一些方法可以使您的provider 的生命周期范圍成為request-scoped。您可以在這里閱讀更多關(guān)于這些技術(shù)的信息。
Custom providers
Nest有一個內(nèi)置的控制反轉(zhuǎn)(“IoC”)容器,用于解決providers之間的關(guān)系。這個特性是上面描述的依賴注入特性的基礎(chǔ),但實(shí)際上比我們目前描述的功能強(qiáng)大得多。@Injectable()裝飾器只是冰山一角,并不是定義providers的唯一方法。實(shí)際上,您可以使用純值、類以及異步或同步工廠。這里提供了更多的例子。
Optional providers
有時候,您可能有一些不需要解決的依賴關(guān)系。例如,您的類可能依賴于configuration object,但是如果沒有傳遞,則應(yīng)該使用默認(rèn)值。在這種情況下,依賴項是可選的,因為缺少配置提供程序不會導(dǎo)致錯誤。
要指示提供程序是可選的,請在constructor簽名中使用@Optional()裝飾器。
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(
@Optional() @Inject('HTTP_OPTIONS') private readonly httpClient: T
) {}
}
注意,在上面的示例中,我們使用的是自定義提供程序,這就是我們包含HTTP_OPTIONS自定義token的原因。前面的示例顯示了基于構(gòu)造函數(shù)的注入,它通過構(gòu)造函數(shù)中的類指示依賴關(guān)系。有關(guān)自定義提供程序及其關(guān)聯(lián)令牌的更多信息,請在這里閱讀。
Property-based injection
我們目前使用的技術(shù)稱為基于構(gòu)造函數(shù)的注入,因為提供者是通過構(gòu)造函數(shù)方法注入的。在某些非常特殊的情況下,property-based injection可能是有用的。例如,如果您的頂級類依賴于一個或多個提供者,那么通過在構(gòu)造函數(shù)的子類中調(diào)用super()來傳遞它們將非常單調(diào)乏味。為了避免這種情況,您可以在屬性級別使用@Inject()裝飾器。
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
警告
如果您的類不擴(kuò)展其他提供程序,您應(yīng)該始終選擇使用constructor-based的注入。
Provider registration
現(xiàn)在我們已經(jīng)定義了一個provider (CatsService),并且有了該服務(wù)的消費(fèi)者(CatsController),我們需要向Nest注冊該服務(wù),以便它能夠執(zhí)行注入。我們通過編輯模塊文件(app.module.ts)并將服務(wù)添加到@Module()裝飾器的providers數(shù)組中來實(shí)現(xiàn)這一點(diǎn)。
// app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class ApplicationModule {}
Nest現(xiàn)在將能夠解決CatsController類的依賴關(guān)系。
下面是我們的目錄結(jié)構(gòu):
