NestJS04-Providers

397 阅读5分钟

Providers是NestJS的一个基本概念,可以让服务、存储库、工厂、helpers等等作为提供服务的一方进行依赖注入等操作,这也是我觉得Nest比较好开发和维护的原因。

在上一章中,我们构建了一个简单的CatsController控制器处理HTTP请求,并将更复杂的任务(业务逻辑,表更新等)委托给ProvidersProviders是在Module中声明为Providers的纯JavaScript类。

依赖注入和控制反转

首先希望大家了解下依赖注入控制反转,有不知道这个概念的朋友们请看浅谈控制反转与依赖注入,这篇文章通俗易懂。

Services

  • Service主要是来做业务的逻辑,比如存储和检索。它可以作为一个Providers提供给Controllers等。
  • 用之前章节的方法,创建一个工程,再创建Cats的module和Controller,然后输入以下命令,来创建一个Service
# 生成service文件,并且不生成测试文件
nest g s cats --no-spec
  • 在下图位置会生成一个Service文件。

service.jpg

  • 再创建一个Interfaces文件夹和cats.interface.ts文件,代码如下
export interface Cat {
  name: string;
  age: number;
  breed: string;
}
  • Service代码如下,需要用@Injectable装饰器来装饰,它声明CatsService是一个可以由Nest IoC容器管理的类。注:Ioc是控制反转的缩写
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;
  }
}
  • 这样在Controller里面就可以通过注入的方式来调用Service,请注意构造函数注入时要加上private
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 {
  // 没有通过New来创建一个实例而是通过构造函数传入参数的方法来注入Service对象
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    // 通过this来访问注入的对象
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    // 通过this来访问注入的对象
    return this.catsService.findAll();
  }
}

依赖注入

Nest是围绕强设计模式构建的,通常称为依赖注入。

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

constructor(private catsService: CatsService) {}

作用域

提供程序通常具有与应用程序生命周期同步的生命周期。当应用程序启动时,必须解析每个依赖项,因此必须实例化每个提供程序。同样,当应用程序关闭时,每个提供程序都将被销毁。然而,也有一些方法可以使您的提供者生存期请求具有作用域。您可以在这里阅读有关这些技术的更多信息。

自定义 providers

Nest有一个内置的控制反转(“IoC”)容器,用于解决提供者之间的关系。这一特性是上述依赖注入特性的基础,但实际上比我们目前所描述的功能强大得多。定义提供程序有几种方法:可以使用纯值、类以及异步或同步工厂。此处提供了更多示例。

可选 providers

有时,您可能有不一定要解决的依赖关系。例如,您的类可能依赖于配置对象,但如果没有传递,则应使用默认值。在这种情况下,依赖项变得可选,因为缺少配置提供程序不会导致错误或者异常。

要表示提供程序是可选的,请在构造函数的签名中使用@optional()装饰器

import { Injectable, Optional, Inject } from '@nestjs/common';  
  
@Injectable()  
export class HttpService<T> {  
  // 可选参数
  constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}  
}

请注意,在上面的示例中,我们使用的是自定义Providers,这就是我们包含HTTP_OPTIONS自定义令牌的原因。前面的示例显示了基于构造函数的注入,通过构造函数中的类指示依赖关系。在此处阅读有关自定义提供程序及其相关令牌的更多信息。

基于属性的注入

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

import { Injectable, Inject } from '@nestjs/common';  
  
@Injectable()  
export class HttpService<T> {  
  @Inject('HTTP_OPTIONS')  
  private readonly httpClient: T;  
}
警告:如果您的类没有扩展另一个Providers,您应该始终使用基于构造函数的注入。

Provider 注册

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

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService]
})
export class CatsModule {}

手动实例化

到目前为止,我们已经讨论了Nest如何自动处理解决依赖关系的大部分细节。在某些情况下,您可能需要跳出内置的依赖注入系统,手动检索或实例化提供程序。我们将在下面简要讨论两个这样的主题。

要获取现有实例或动态实例化提供程序,可以使用Module引用

要在bootstrap()函数中获取提供程序(例如,对于没有控制器的独立应用程序,或在引导过程中使用配置服务),请参阅独立应用程序

代码地址

代码