NestJS依赖注入 #
什么是依赖注入? #
依赖注入(Dependency Injection,DI)是一种设计模式,它将依赖的创建和管理责任从类本身转移到外部容器。NestJS内置了一个强大的IoC(Inversion of Control,控制反转)容器来管理依赖。
传统方式 vs 依赖注入 #
typescript
// 传统方式 - 类自己创建依赖
class UsersController {
private usersService = new UsersService();
}
// 依赖注入 - 依赖由外部提供
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
}
依赖注入的优势 #
| 优势 | 说明 |
|---|---|
| 解耦 | 类不依赖具体实现,只依赖接口 |
| 可测试 | 易于模拟依赖进行单元测试 |
| 可维护 | 依赖关系清晰,易于理解和修改 |
| 可复用 | 组件可以在不同上下文中复用 |
基本注入 #
构造函数注入 #
最常用的注入方式:
typescript
@Injectable()
export class UsersService {
constructor(
private readonly databaseService: DatabaseService,
private readonly loggerService: LoggerService,
) {}
}
属性注入 #
使用@Inject()装饰器:
typescript
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class UsersService {
@Inject('CONFIG_OPTIONS')
private configOptions: ConfigOptions;
}
@Inject装饰器 #
注入自定义Token #
typescript
providers: [
{
provide: 'API_KEY',
useValue: 'your-api-key',
},
]
// 使用
constructor(
@Inject('API_KEY') private apiKey: string,
) {}
注入接口 #
由于TypeScript接口在编译后被移除,需要使用Token:
typescript
export interface LoggerInterface {
log(message: string): void;
}
providers: [
{
provide: 'LoggerInterface',
useClass: ConsoleLogger,
},
]
// 使用
constructor(
@Inject('LoggerInterface') private logger: LoggerInterface,
) {}
注入器(Injector) #
NestJS的IoC容器负责:
text
┌─────────────────────────────────────────────────────────────┐
│ IoC 容器 │
├─────────────────────────────────────────────────────────────┤
│ 1. 解析依赖关系 │
│ 2. 创建依赖实例 │
│ 3. 注入到目标类 │
│ 4. 管理实例生命周期 │
└─────────────────────────────────────────────────────────────┘
模块引用(ModuleRef) #
ModuleRef提供了动态访问模块内提供者的能力:
获取提供者实例 #
typescript
import { Injectable, OnModuleInit, ModuleRef } from '@nestjs/common';
@Injectable()
export class UsersService implements OnModuleInit {
private service: SomeService;
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
this.service = this.moduleRef.get(SomeService);
}
}
动态解析 #
typescript
@Injectable()
export class UsersService {
constructor(private moduleRef: ModuleRef) {}
async process(type: string) {
const service = this.moduleRef.get(
type === 'email' ? EmailService : SmsService,
);
return service.send();
}
}
解析瞬态提供者 #
typescript
@Injectable()
export class UsersService {
constructor(private moduleRef: ModuleRef) {}
createLogger() {
// 每次调用创建新实例
const logger = this.moduleRef.resolve(TransientLoggerService);
return logger;
}
}
注册动态提供者 #
typescript
@Injectable()
export class UsersService {
constructor(private moduleRef: ModuleRef) {}
async registerProvider() {
const dynamicModule = await this.moduleRef.create(
DynamicModule.forFeature({ name: 'dynamic' }),
);
const service = dynamicModule.get(DynamicService);
}
}
循环依赖 #
问题场景 #
typescript
// users.service.ts
@Injectable()
export class UsersService {
constructor(private authService: AuthService) {}
}
// auth.service.ts
@Injectable()
export class AuthService {
constructor(private usersService: UsersService) {}
}
使用forwardRef解决 #
typescript
// users.service.ts
import { forwardRef } from '@nestjs/common';
@Injectable()
export class UsersService {
constructor(
@Inject(forwardRef(() => AuthService))
private authService: AuthService,
) {}
}
// auth.service.ts
@Injectable()
export class AuthService {
constructor(
@Inject(forwardRef(() => UsersService))
private usersService: UsersService,
) {}
}
模块级别的forwardRef #
typescript
@Module({
imports: [forwardRef(() => AuthModule)],
})
export class UsersModule {}
生命周期钩子 #
提供者可以实现以下生命周期钩子:
typescript
import {
Injectable,
OnModuleInit,
OnModuleDestroy,
BeforeApplicationShutdown,
OnApplicationShutdown,
} from '@nestjs/common';
@Injectable()
export class UsersService implements
OnModuleInit,
OnModuleDestroy,
BeforeApplicationShutdown,
OnApplicationShutdown {
onModuleInit() {
console.log('模块初始化完成');
}
onModuleDestroy() {
console.log('模块即将销毁');
}
beforeApplicationShutdown(signal?: string) {
console.log(`应用即将关闭,信号: ${signal}`);
}
onApplicationShutdown(signal?: string) {
console.log(`应用已关闭,信号: ${signal}`);
}
}
生命周期顺序 #
text
1. onModuleInit()
2. onApplicationBootstrap()
... 应用运行 ...
3. onModuleDestroy()
4. beforeApplicationShutdown()
5. onApplicationShutdown()
可选依赖 #
使用@Optional()装饰器:
typescript
import { Injectable, Optional } from '@nestjs/common';
@Injectable()
export class UsersService {
constructor(
@Optional()
private readonly logger?: LoggerService,
) {}
doSomething() {
this.logger?.log('Doing something');
}
}
属性注入 #
除了构造函数注入,还可以使用属性注入:
typescript
import { Injectable, Inject, Optional } from '@nestjs/common';
@Injectable()
export class UsersService {
@Inject('CONFIG_OPTIONS')
@Optional()
private configOptions: ConfigOptions;
@Inject(LoggerService)
private logger: LoggerService;
}
提供者作用域详解 #
单例作用域(默认) #
typescript
@Injectable()
export class UsersService {
// 整个应用生命周期内只有一个实例
}
请求作用域 #
typescript
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class UsersService {
// 每个HTTP请求创建一个新实例
}
瞬态作用域 #
typescript
@Injectable({ scope: Scope.TRANSIENT })
export class UsersService {
// 每次注入都创建一个新实例
}
作用域继承 #
typescript
@Injectable({ scope: Scope.REQUEST })
export class UsersService {}
@Injectable()
export class AuthService {
// 必须也是REQUEST作用域
constructor(private usersService: UsersService) {}
}
条件模块 #
使用条件导入:
typescript
import { Module } from '@nestjs/common';
@Module({})
export class DatabaseModule {
static register(options: DatabaseOptions) {
const providers = [];
if (options.type === 'mysql') {
providers.push(MySqlService);
} else if (options.type === 'postgres') {
providers.push(PostgresService);
}
return {
module: DatabaseModule,
providers,
exports: providers,
};
}
}
动态模块注入 #
typescript
import { Module, DynamicModule, Provider } from '@nestjs/common';
@Module({})
export class ConfigModule {
static forRoot(options: ConfigOptions): DynamicModule {
const configProvider: Provider = {
provide: 'CONFIG_OPTIONS',
useValue: options,
};
return {
module: ConfigModule,
providers: [configProvider, ConfigService],
exports: [ConfigService],
global: options.isGlobal,
};
}
}
// 使用
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true })],
})
export class AppModule {}
依赖注入最佳实践 #
1. 依赖接口而非实现 #
typescript
// 定义接口
export interface ILogger {
log(message: string): void;
}
// 提供实现
providers: [
{
provide: 'ILogger',
useClass: ConsoleLogger,
},
]
// 注入接口
constructor(
@Inject('ILogger') private logger: ILogger,
) {}
2. 使用Token常量 #
typescript
export const LOGGER_TOKEN = Symbol('LOGGER');
providers: [
{
provide: LOGGER_TOKEN,
useClass: ConsoleLogger,
},
]
3. 避免循环依赖 #
重构代码结构,提取共享逻辑到独立服务:
typescript
// 提取共享逻辑
@Injectable()
export class SharedService {}
@Injectable()
export class UsersService {
constructor(private sharedService: SharedService) {}
}
@Injectable()
export class AuthService {
constructor(private sharedService: SharedService) {}
}
4. 合理使用作用域 #
- 默认使用单例作用域
- 只有需要请求隔离时才使用请求作用域
- 避免过度使用瞬态作用域
总结 #
本章深入学习了NestJS依赖注入系统:
- 依赖注入的概念和优势
- 各种注入方式
- ModuleRef的使用
- 循环依赖的处理
- 生命周期钩子
- 提供者作用域
接下来,让我们学习 路由基础。
最后更新:2026-03-28