NestJS 依赖注入方式全解
一、基础注入方式
1. 构造函数注入(Constructor Injection)
适用场景:模块间依赖传递,服务初始化时必须存在的依赖
实现方式:通过构造函数参数声明依赖,NestJS 自动解析并注入
@Injectable()
class UserService {constructor(private readonly logger: LoggerService) {} // 自动注入 LoggerServicegetUser() {this.logger.log('Fetching user data');// 业务逻辑}
}
2. 属性注入(Property Injection)
适用场景:可选依赖或动态注入场景
实现方式:使用 @Inject()
装饰器配合自定义 Token
@Injectable()
class OrderService {@Inject('PAYMENT_GATEWAY') // 自定义 Tokenprivate paymentGateway: PaymentService;processPayment() {this.paymentGateway.charge(); // 动态注入的支付网关}
}
二、高级注入模式
3. 值提供者(Value Provider)
适用场景:注入配置常量、第三方库实例
实现方式:通过 useValue
定义固定值
@Module({providers: [{provide: 'API_KEY',useValue: '12345-ABCDE', // 硬编码 API Key},{provide: 'EXTERNAL_SDK',useValue: new ThirdPartySDK(), // 注入第三方实例}]
})
class ConfigModule {}
4. 类提供者(Class Provider)
适用场景:根据环境动态切换实现类
实现方式:使用 useClass
指定具体类
@Module({providers: [{provide: 'ConfigService',useClass: process.env.NODE_ENV === 'prod' ? ProdConfigService : DevConfigService,}]
})
class AppModule {}
5. 工厂提供者(Factory Provider)
适用场景:需要运行时计算或组合依赖的场景
实现方式:通过 useFactory
动态创建实例
@Module({providers: [{provide: 'DatabaseConnection',useFactory: async (configService: ConfigService) => {const config = await configService.getDatabaseConfig();return createConnection(config);},inject: [ConfigService], // 注入其他依赖}]
})
class DatabaseModule {}
6. 异步提供者(Async Provider)
适用场景:处理异步初始化操作(如数据库连接)
实现方式:结合 useFactory
与 async/await
@Module({providers: [{provide: 'RedisClient',useFactory: async () => {const client = createClient({ url: 'redis://localhost:6379' });await client.connect();return client;},}]
})
class CacheModule {}
7. 多提供者(Multi Provider)
适用场景:插件系统、中间件集合等需要多个同类实现的场景
实现方式:设置 multi: true
标记
@Module({providers: [{provide: 'EventListeners',useClass: OrderListener,multi: true,},{provide: 'EventListeners',useClass: PaymentListener,multi: true,}]
})
class EventModule {}
三、最佳实践与常见问题
1. 模块化设计原则
- 单一职责模块:每个模块聚焦特定功能领域(如
UserModule
、AuthModule
) - 跨模块依赖:通过
exports
暴露服务,避免循环导入@Module({providers: [UserService],exports: [UserService], // 其他模块可注入 UserService }) class UserModule {}
2. 测试优化策略
- 模拟依赖:使用
@nestjs/testing
创建隔离测试环境const module = await Test.createTestingModule({providers: [UserService,{ provide: LoggerService, useValue: mockLogger },], }).compile();
3. 性能优化技巧
- 延迟加载:对非关键依赖使用
@LazyInject()
(需第三方库支持) - 作用域控制:通过
@Injectable({ scope: Scope.TRANSIENT })
管理实例生命周期
4. 常见问题解析
- 循环依赖:使用
forwardRef()
打破循环@Module({imports: [forwardRef(() => OrderModule)], }) class UserModule {}
- Token 冲突:优先使用类名作为 Token,避免字符串 Token 重复
四、实战案例:TypeORM 集成
@Module({imports: [TypeOrmModule.forRootAsync({useFactory: (config: ConfigService) => ({type: 'postgres',url: config.get('DATABASE_URL'),entities: [__dirname + '/**/*.entity{.ts,.js}'],}),inject: [ConfigService],}),TypeOrmModule.forFeature([UserEntity]),],providers: [UserService],
})
class UserModule {}@Injectable()
class UserService {constructor(@InjectRepository(UserEntity)private userRepo: Repository<UserEntity>,) {}async findUser(id: string) {return this.userRepo.findOne(id);}
}
五、总结
NestJS 的依赖注入系统通过多样化的注入方式和灵活的提供者配置,为开发者提供了强大的架构设计能力。从基础的构造函数注入到复杂的工厂提供者,每种模式都针对特定场景进行了优化。掌握这些模式后,开发者可以:
- 实现模块间低耦合设计
- 提升代码可测试性与可维护性
- 动态适配不同运行环境
- 构建可扩展的插件化架构
建议在实际项目中结合具体场景选择注入方式,并利用 NestJS 的模块化特性构建清晰的应用架构。