NestJS 装饰器与依赖注入(DI)的工作原理
请解释 NestJS 中装饰器(@Module、@Controller、@Injectable)如何工作,以及依赖注入容器如何解析提供者的依赖关系(constructor injection)。
回答
屠龙少年
装饰器作用:
@Module({ imports, controllers, providers }):标记模块类,收集元数据@Controller('path'):标记路由控制器,注册路由@Injectable():标记可被 DI 容器管理的类(服务、守护者、工厂等)@Inject(token):显式注入令牌(非 class 提供者)
依赖注入流程:
- 模块编译:Nest 扫描
@Module元数据,构建模块依赖图 - 收集 Provider:注册 providers 到 DI 容器(Map<token, factory>)
- 解析依赖:实例化时,通过 TypeScript 编译后的
design:paramtypes元数据获取构造函数参数类型 - 递归查找:若参数类型也在容器中注册过,递归实例化其依赖
- 单例/作用域:默认单例(Singleton),
{ scope: Scope.REQUEST }或Scope.TRANSIENT
@Injectable()
export class UserService {
constructor(private readonly userRepo: UserRepository) {}
// userRepo 由 DI 容器自动注入
}
Hack 原理:Nest 使用 reflect-metadata 库,通过 Reflect.getMetadata('design:paramtypes', target) 获取参数类型数组。然后递归 resolve 每个类型,用 new target(...deps) 实例化。
自定义 Provider:
@Module({
providers: [
{ provide: 'STRING_TOKEN', useValue: 'hello' },
{ provide: 'FACTORY', useFactory: (dep) => ..., inject: [Dep] },
]
})