NestJS 源码解析:依赖注入系统
深入 Injector 和 Container,揭秘 NestJS 的 IoC 实现。
依赖注入基础
NestJS 的依赖注入基于 TypeScript 装饰器和 reflect-metadata:
typescript
@Injectable()
export class CatsService {
constructor(private readonly logger: LoggerService) {}
}
编译后,TypeScript 会生成元数据,记录构造函数参数类型。
核心组件
1. InstanceWrapper
包装每个可注入的实例:
typescript
// packages/core/injector/instance-wrapper.ts
export class InstanceWrapper<T = any> {
public readonly name: string;
public readonly token: InjectionToken;
public readonly metatype: Type<T>;
public readonly scope?: Scope;
private readonly values = new WeakMap<ContextId, InstancePerContext<T>>();
// 获取实例
get instance(): T {
const instancePerContext = this.getInstanceByContextId(STATIC_CONTEXT);
return instancePerContext.instance;
}
// 设置实例
set instance(value: T) {
const instancePerContext = this.getInstanceByContextId(STATIC_CONTEXT);
instancePerContext.instance = value;
}
// 获取构造函数元数据
public getCtorMetadata(): InstanceWrapper[] {
return this.inject || [];
}
}
2. Module
模块类,管理 providers、controllers、imports、exports:
typescript
// packages/core/injector/module.ts
export class Module {
private readonly _providers = new Map<InjectionToken, InstanceWrapper<Injectable>>();
private readonly _controllers = new Map<InjectionToken, InstanceWrapper<Controller>>();
private readonly _imports = new Set<Module>();
private readonly _exports = new Set<InjectionToken>();
// 添加 Provider
public addProvider(provider: Provider): string {
if (this.isCustomProvider(provider)) {
return this.addCustomProvider(provider);
}
const token = provider as Type<Injectable>;
const wrapper = new InstanceWrapper({
token,
name: token.name,
metatype: token,
instance: null,
isResolved: false,
scope: getClassScope(token),
host: this,
});
this._providers.set(token, wrapper);
return token.name;
}
// 添加自定义 Provider
private addCustomProvider(provider: Provider): string {
if (this.isCustomClass(provider)) {
return this.addCustomClass(provider as ClassProvider);
}
if (this.isCustomValue(provider)) {
return this.addCustomValue(provider as ValueProvider);
}
if (this.isCustomFactory(provider)) {
return this.addCustomFactory(provider as FactoryProvider);
}
if (this.isCustomUseExisting(provider)) {
return this.addCustomUseExisting(provider as ExistingProvider);
}
}
}
3. Injector
依赖注入核心引擎:
typescript
// packages/core/injector/injector.ts
export class Injector {
// 加载实例
public async loadInstance<T>(
wrapper: InstanceWrapper<T>,
collection: Map<InjectionToken, InstanceWrapper>,
moduleRef: Module,
contextId = STATIC_CONTEXT,
inquirer?: InstanceWrapper,
) {
const { token } = wrapper;
// 检查是否已解析
const instanceHost = wrapper.getInstanceByContextId(contextId, inquirer?.id);
if (instanceHost.isResolved) {
return instanceHost.instance;
}
// 解析构造函数依赖
const dependencies = await this.resolveConstructorParams<T>(
wrapper,
moduleRef,
contextId,
inquirer,
);
// 实例化
const instance = await this.instantiateClass(
dependencies,
wrapper,
wrapper.inject,
contextId,
inquirer,
);
// 注入属性依赖
await this.loadPropertiesOnInstance(instance, wrapper, moduleRef, contextId);
return instance;
}
// 解析构造函数参数
public async resolveConstructorParams<T>(
wrapper: InstanceWrapper<T>,
moduleRef: Module,
contextId: ContextId,
inquirer?: InstanceWrapper,
): Promise<unknown[]> {
// 获取依赖元数据
const dependencies = this.getCtorDependencies(wrapper);
// 并行解析所有依赖
return Promise.all(
dependencies.map(async (dependency, index) => {
const { wrapper: instanceWrapper } = await this.lookupComponent(
dependency,
moduleRef,
contextId,
wrapper,
index,
);
return instanceWrapper.instance;
}),
);
}
// 查找依赖
public async lookupComponent(
dependency: InjectorDependency,
moduleRef: Module,
contextId: ContextId,
wrapper: InstanceWrapper,
index: number,
) {
// 1. 在当前模块查找
const instanceWrapper = await this.lookupComponentInParentModules(
dependency,
moduleRef,
contextId,
wrapper,
index,
);
if (instanceWrapper) {
return { wrapper: instanceWrapper };
}
// 2. 在全局模块查找
const globalWrapper = await this.lookupComponentInGlobalModules(
dependency,
contextId,
wrapper,
);
if (globalWrapper) {
return { wrapper: globalWrapper };
}
// 3. 未找到,抛出异常
throw new UndefinedDependencyException(wrapper.name, dependency, index);
}
}
依赖解析流程
typescript
@Injectable()
class CatsService {
constructor(
private logger: LoggerService,
private config: ConfigService,
) {}
}
解析流程:
┌─────────────────────────────────────────────────────┐
│ 1. 获取构造函数参数类型 │
│ Reflect.getMetadata('design:paramtypes', target) │
│ → [LoggerService, ConfigService] │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 2. 遍历每个依赖,调用 lookupComponent │
│ → 在当前模块 providers 中查找 │
│ → 在 imports 的模块 exports 中查找 │
│ → 在全局模块中查找 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 3. 递归解析依赖的依赖 │
│ LoggerService 可能也有自己的依赖 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 4. 实例化 │
│ new CatsService(loggerInstance, configInstance) │
└─────────────────────────────────────────────────────┘
Barrier 并行解析机制
源码中使用 Barrier 实现依赖的并行解析:
typescript
// packages/core/injector/injector.ts
const paramBarrier = new Barrier(dependencies.length);
const resolveParam = async (param: unknown, index: number) => {
try {
const paramWrapper = await this.resolveSingleParam<T>(/*...*/);
// 等待所有依赖都解析完成
await paramBarrier.signalAndWait();
const paramWrapperWithInstance = await this.resolveComponentHost(/*...*/);
return instanceHost?.instance;
} catch (err) {
paramBarrier.signal(); // 出错也要释放信号
// ...
}
};
const instances = await Promise.all(dependencies.map(resolveParam));
Barrier 确保所有依赖的 InstanceWrapper 都被解析后,才开始获取实例。这避免了依赖树静态性判断错误导致的 undefined 注入问题。
SettlementSignal 循环依赖检测
typescript
// packages/core/injector/injector.ts
if (instanceHost.isPending) {
const settlementSignal = wrapper.settlementSignal;
if (inquirer && settlementSignal?.isCycle(inquirer.id)) {
throw new CircularDependencyException(`"${wrapper.name}"`);
}
return instanceHost.donePromise!.then((err?: unknown) => {
if (err) throw err;
});
}
SettlementSignal 追踪依赖解析链,当检测到循环时抛出 CircularDependencyException。
作用域
NestJS 支持三种作用域:
typescript
export enum Scope {
DEFAULT = 0, // 单例
TRANSIENT = 1, // 每次注入创建新实例
REQUEST = 2, // 每个请求创建新实例
}
单例 (DEFAULT)
typescript
@Injectable()
export class CatsService {}
整个应用生命周期只有一个实例。
瞬态 (TRANSIENT)
typescript
@Injectable({ scope: Scope.TRANSIENT })
export class LoggerService {}
每次注入都创建新实例:
typescript
// packages/core/injector/injector.ts
if (wrapper.scope === Scope.TRANSIENT) {
return this.instantiateClass(
dependencies,
wrapper,
wrapper.inject,
contextId,
inquirer,
);
}
请求作用域 (REQUEST)
typescript
@Injectable({ scope: Scope.REQUEST })
export class RequestService {}
每个 HTTP 请求创建新实例:
typescript
// packages/core/router/router-execution-context.ts
if (wrapper.scope === Scope.REQUEST) {
const contextId = createContextId();
const instance = await this.injector.loadInstance(
wrapper,
collection,
moduleRef,
contextId,
);
return instance;
}
自定义 Provider
useClass
typescript
{
provide: CatsService,
useClass: MockCatsService,
}
useValue
typescript
{
provide: 'CONFIG',
useValue: { apiKey: 'xxx' },
}
useFactory
typescript
{
provide: 'ASYNC_CONNECTION',
useFactory: async (config: ConfigService) => {
return await createConnection(config.get('database'));
},
inject: [ConfigService],
}
useExisting
typescript
{
provide: 'AliasService',
useExisting: CatsService,
}
循环依赖处理
NestJS 使用 forwardRef 处理循环依赖:
typescript
@Injectable()
export class CatsService {
constructor(
@Inject(forwardRef(() => DogsService))
private dogsService: DogsService,
) {}
}
实现原理:
typescript
// packages/common/utils/forward-ref.util.ts
export const forwardRef = (fn: () => any): ForwardReference => ({
forwardRef: fn,
});
// packages/core/injector/injector.ts
private resolveParamToken(param: any) {
if (param && param.forwardRef) {
return param.forwardRef();
}
return param;
}
属性注入
除了构造函数注入,还支持属性注入:
typescript
@Injectable()
export class CatsService {
@Inject(LoggerService)
private readonly logger: LoggerService;
}
实现:
typescript
// packages/core/injector/injector.ts
public async loadPropertiesOnInstance(
instance: object,
wrapper: InstanceWrapper,
moduleRef: Module,
contextId: ContextId,
) {
const properties = wrapper.getPropertiesMetadata();
for (const property of properties) {
const { key, wrapper: propertyWrapper } = await this.lookupComponent(
property.name,
moduleRef,
contextId,
wrapper,
);
instance[key] = propertyWrapper.instance;
}
}
总结
NestJS 依赖注入系统的核心:
- 元数据驱动 :通过
reflect-metadata获取类型信息 - InstanceWrapper:包装每个可注入实例
- Module:管理 providers、controllers、imports、exports
- Injector:递归解析依赖,实例化对象
- 作用域:支持单例、瞬态、请求三种作用域
- 循环依赖 :通过
forwardRef延迟解析
下一篇我们将分析模块系统的实现。
📦 源码位置:
packages/core/injector/下一篇:NestJS 模块系统