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 |
注意事项
- 所有注入都是通过providers
- 令牌需要唯一,非服务类的令牌需要唯一,最好就是用
Symbol - 注入方式,非服务类都是通过
@Inject(token)显式注入 - 异步,只有
useFactory才支持异步