005.nestjs基础(1)providers

51 阅读4分钟

Custom providers

依赖注入 DI(Dependecy injection) 是 控制反转 IOC (inversion of control)的一种技术,将以来的实例委托给到IOC容器,而不是在自己的代码中强制执行。

提供者是 NestJS 依赖注入系统的核心。除了标准的类提供者,还支持多种自定义方式。

第一步:定义一个提供者 provider

在类上面 写上@Injectable()装饰器,把类标记为提供者

import { Injectable } from "@nestjs/common";

@Injectable()
export class TestService {
  get(): string {
    return "Hello World!";
  }
}

第二步:使用 放在构造函数上

import { Controller, Get } from "@nestjs/common";
import { TestService } from "./test.service";

@Controller("test")
export class TestController {
  // 直接放在构造函数上
  constructor(private testService: TestService) {}

  @Get()
  get(): string {
    return this.testService.get();
  }
}

第三步:注册--全局注册

import { Module } from "@nestjs/common";
import { TestController } from "./test/test.controller";
import { TestService } from "./test/test.service";

@Module({
  imports: [
   
  ],
  controllers: [TestController],
  providers: [
   
    TestService,
  ],
})
export class AppModule {}

标准提供者 (Standard providers)

import { Module } from "@nestjs/common";
import { TestController } from "./test/test.controller";
import { TestService } from "./test/test.service";

@Module({
  imports: [
   
  ],
  controllers: [TestController],
  // 方法1
   providers: [
   
    TestService,
  ],
  // 方法2
  providers: [
   
      { provide: TestService, useClass: TestService },
  ],
})
export class AppModule {}

值提供者(Value providers) useValue

useValue 语法适用于注入常数值、将外部库放入 Nest 容器,或用模拟对象替换真实实现


export const APP_CONFIG = {
  port: 3000,
  env: 'development',
  db: {
    host: 'localhost',
    port: 5432,
  },
};

// 定义唯一令牌(推荐用 Symbol 避免命名冲突)
export const APP_CONFIG_TOKEN = Symbol('APP_CONFIG');


// src/app.module.ts
import { Module } from '@nestjs/common';
import { APP_CONFIG, APP_CONFIG_TOKEN } from './config/app.config';
import { AppService } from './app.service';

@Module({
  providers: [
    AppService,
    // 注册值提供者:令牌 -> 值
    {
      provide: APP_CONFIG_TOKEN, // 令牌(唯一标识)
      useValue: APP_CONFIG,      // 实际注入的值
    },
  ],
})
export class AppModule {}


非类提供者令牌(Non-class-based provider tokens)

// 提供者
//支持字符串、Symbol、枚举作为令牌:
{ provide: "testService", useValue: new TestService() },
 
 // 使用者
  constructor(@Inject("testService") private testService: TestService) {}


 

类提供者 (Class Providers) useClass

定义抽象类(令牌)

export abstract class LoggerAbstract {
  abstract log(message: string): void;
}

实现类

// src/loggers/dev.logger.ts
export class DevLogger implements LoggerAbstract {
  log(message: string): void {
    console.log(`[DEV] ${message}`);
  }
}

// src/loggers/prod.logger.ts
export class ProdLogger implements LoggerAbstract {
  log(message: string): void {
    console.log(`[PROD] ${message}`);
  }
}

动态注册

providers: [
    {
      provide: LoggerAbstract, // 令牌是抽象类
      useClass: APP_CONFIG.env === 'development' ? DevLogger : ProdLogger, // 动态选择具体类
    },
  ],

注入使用

constructor(private readonly logger: LoggerAbstract) {}

工厂供应商 (Factory providers) useFactory

定义工厂

// src/providers/db.providers.ts
import { createConnection } from 'typeorm';
import { APP_CONFIG_TOKEN } from '../config/app.config';

// 令牌
export const DB_CONNECTION_TOKEN = Symbol('DB_CONNECTION');

// 工厂提供者:根据配置创建数据库连接
export const dbConnectionProvider = {
  provide: DB_CONNECTION_TOKEN,
  useFactory: (appConfig) => { // 工厂函数,参数是 inject 的依赖
    return createConnection({
      type: 'postgres',
      host: appConfig.db.host,
      port: appConfig.db.port,
      username: 'postgres',
      password: '123456',
      database: 'nest-db',
    });
  },
  inject: [APP_CONFIG_TOKEN], // 声明工厂函数依赖的 Provider 令牌(顺序对应参数)
};

动态注册

 providers: [
    { provide: APP_CONFIG_TOKEN, useValue: APP_CONFIG },
    dbConnectionProvider, // 注册工厂提供者
  ],

注入使用

constructor(@Inject(DB_CONNECTION_TOKEN) private readonly dbConnection: Connection) {}

异步提供者 (Asynchronous providers)

{
  provide: 'ASYNC_CONNECTION',
  useFactory: async () => { // 就是useFactory
    const connection = await createConnection(options);
    return connection;
  },
}

别名提供者 (Alias providers )useExisting

在现有的类 提供一个别名

   {TestService,
    { provide: "testService", useExisting: TestService },
    }

// 使用
constructor(
    private readonly testService1: TestService, // 原令牌
    @Inject('testService') private readonly testService2: TestService, // 别名令牌
  ) {}

出口定制服务商(Export custom provider)

exports: ['CONNECTION']  // 使用 token
// 或
exports: [connectionProvider]  // 使用完整对象

作用域(Scopes)

作用域说明场景内存占用情况
DEFAULT单例,并且是默认的,应用启动时创建,全局用配置,工具类,数据库连接池
REQUEAT每个请求都会专门创建一个实例。请求完成后,实例会被回收。当前用户上下文
TRANSIENT瞬时,每次注入创建新的实例请求日志追踪

用法

// 1  给injectable装饰器
@Injectable({ scope: Scope.REQUEST })

// 2 注册时候
{
  provide: 'CACHE_MANAGER',
  useClass: CacheManager,
  scope: Scope.TRANSIENT,
}

总结一下

服务类:令牌=类本身 直接@Injectable(),直接使用就行

非服务类:4种,令牌自定义,使用的时候需要使用Inject(token),token可以是(Symbol,对象,字符串)

Provider类型核心用途场景关键字
Value providers注入常量、配置数据库配置useValue
Factory Provider动态创建,异步逻辑,依赖其他的Provider数据库连接,读取useFactory
Class Provider类替换、别名多环境配置、策略模式useClass
Existing Provider为以后的Provider创建别名兼容老代码,统一模块访问标识useExisting

注意事项

  1. 所有注入都是通过providers
  2. 令牌需要唯一,非服务类的令牌需要唯一,最好就是用Symbol
  3. 注入方式,非服务类都是通过@Inject(token)显式注入
  4. 异步,只有useFactory才支持异步