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