Nest(四):Providers

译自:https://docs.nestjs.com/providers

Providers是Nest中的一个基本概念。许多基本的嵌套类可以被视为Providers——services、repositories、factories、helpers等等。Providers的主要思想是它可以注入依赖项;这意味着对象可以彼此创建各种关系,并且对象的“连接”实例的功能在很大程度上可以委托给Nest运行时系统。Providers只是一个用@Injectable()装饰器注释的类。

image.png

在前一章中,我们构建了一个简单的CatsController。控制器应该处理HTTP请求,并将更复杂的任务委托给Providers。提供者是纯JavaScript类,在类声明之前有一个@Injectable()装饰器。

提示
由于Nest支持以一种更面向对象的方式设计和组织依赖关系,因此我们强烈建议遵循SOLID原则。

Services

让我们从创建一个简单的CatsService开始。该服务将负责数据存储和检索,并被设计为供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创建服务,只需执行$ nest g service cats命令。

我们的CatsService是一个基本类,有一个属性和两个方法。惟一的新特性是它使用@Injectable()装饰器。@Injectable()装饰器附加了元数据,它告诉Nest这个类是一个Nest provider。顺便说一下,这个例子也使用了一个Cat接口,它可能看起来像这样:

export interface Cat {
  name: string;
  age: number;
  breed: string;
}

现在我们有一个服务类来检索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是通过类构造函数注入的。注意使用private readonly语法。这种简写允许我们在同一位置立即声明和初始化catsService成员。

Dependency injection

Nest是围绕强设计模式(通常称为依赖项注入)构建的。我们建议阅读在官方Angular文档中关于这个概念的一篇很棒的文章。

在Nest中,由于TypeScript的功能,管理依赖项非常容易,因为依赖项仅通过类型解析。在下面的示例中,Nest将通过创建和返回catsService实例来解析catsService(或者,在单例的正常情况下,如果已经在其他地方请求了现有实例,则返回它)。该依赖项被解析并传递给控制器的构造函数(或分配给指定的属性):

constructor(private readonly catsService: CatsService) {}

Scopes

Providers 通常有一个与应用程序生命周期同步的生命周期(“范围”)。当应用程序引导时,必须解析每个依赖项,因此必须实例化每个provider 。类似地,当应用程序关闭时,每个provider 都会被销毁。但是,也有一些方法可以使您的provider 的生命周期范围成为request-scoped。您可以在这里阅读更多关于这些技术的信息。

Custom providers

Nest有一个内置的控制反转(“IoC”)容器,用于解决providers之间的关系。这个特性是上面描述的依赖注入特性的基础,但实际上比我们目前描述的功能强大得多。@Injectable()装饰器只是冰山一角,并不是定义providers的唯一方法。实际上,您可以使用纯值、类以及异步或同步工厂。这里提供了更多的例子。

Optional providers

有时候,您可能有一些不需要解决的依赖关系。例如,您的类可能依赖于configuration object,但是如果没有传递,则应该使用默认值。在这种情况下,依赖项是可选的,因为缺少配置提供程序不会导致错误。

要指示提供程序是可选的,请在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的原因。前面的示例显示了基于构造函数的注入,它通过构造函数中的类指示依赖关系。有关自定义提供程序及其关联令牌的更多信息,请在这里阅读。

Property-based injection

我们目前使用的技术称为基于构造函数的注入,因为提供者是通过构造函数方法注入的。在某些非常特殊的情况下,property-based injection可能是有用的。例如,如果您的顶级类依赖于一个或多个提供者,那么通过在构造函数的子类中调用super()来传递它们将非常单调乏味。为了避免这种情况,您可以在属性级别使用@Inject()装饰器。

import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
  @Inject('HTTP_OPTIONS')
  private readonly httpClient: T;
}

警告
如果您的类不扩展其他提供程序,您应该始终选择使用constructor-based的注入。

Provider registration

现在我们已经定义了一个provider (CatsService),并且有了该服务的消费者(CatsController),我们需要向Nest注册该服务,以便它能够执行注入。我们通过编辑模块文件(app.module.ts)并将服务添加到@Module()装饰器的providers数组中来实现这一点。

// 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现在将能够解决CatsController类的依赖关系。

下面是我们的目录结构:


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容

  • 依赖注入(DI -- Dependency Injection)是一种重要的应用设计模式。Angular里面...
    tuacy阅读 13,230评论 4 16
  • 版本:Angular 5.0.0-alpha 依赖注入是重要的应用设计模式。它使用得非常广泛,以至于几乎每个人都称...
    soojade阅读 2,986评论 0 3
  • core package 概要:Core是所有其他包的基础包.它提供了大部分功能包括metadata,templa...
    LOVE小狼阅读 2,559评论 0 3
  • 今天的主题不是雀巢咖啡,也不是雀巢奶粉,毕竟我王境泽就是饿死,也不会打你们一点广告。 前言 近几年由于 Node....
    维特陈先森阅读 2,981评论 4 8
  • 一、什么是依赖注入 控制反转(IoC) 控制反转的概念最早在2004年由Martin Fowler提出,是针对面向...
    Keriy阅读 3,172评论 0 8