Nest.js源码分析系列(一):启动机制及依赖注入
从NestFactory.create开始
从Nest.js的入口main.ts开始分析:
import { AppModule } from './app.module'; import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); await app.listen(3001); console.log(`Application is running on: ${await app.getUrl()}`); } bootstrap(); 复制代码
在bootstrap中首先调用了NestFactory.create(AppModule)
,获取app,源码位于packages/core/nest-factory.ts
:
// packages/core/nest-factory.ts export class NestFactoryStatic { public async create<T extends INestApplication = INestApplication>( module: any, serverOrOptions?: AbstractHttpAdapter | NestApplicationOptions, options?: NestApplicationOptions, ): Promise<T> { const [httpServer, appOptions] = this.isHttpServer(serverOrOptions) ? [serverOrOptions, options] : [this.createHttpAdapter(), serverOrOptions]; const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); this.setAbortOnError(serverOrOptions, options); this.applyLogger(appOptions); await this.initialize(module, container, applicationConfig, httpServer); const instance = new NestApplication( container, httpServer, applicationConfig, appOptions, ); const target = this.createNestInstance(instance); return this.createAdapterProxy<T>(target, httpServer); } } 复制代码
在这里,我们重点关注this.initialize(module, container, applicationConfig, httpServer)
的逻辑:
initialize
private async initialize( module: any, container: NestContainer, config = new ApplicationConfig(), httpServer: HttpServer = null, ) { const instanceLoader = new InstanceLoader(container); const metadataScanner = new MetadataScanner(); const dependenciesScanner = new DependenciesScanner( container, metadataScanner, config, ); container.setHttpAdapter(httpServer); const teardown = this.abortOnError === false ? rethrow : undefined; await httpServer?.init(); try { this.logger.log(MESSAGES.APPLICATION_START); await ExceptionsZone.asyncRun(async () => { await dependenciesScanner.scan(module); await instanceLoader.createInstancesOfDependencies(); dependenciesScanner.applyApplicationProviders(); }, teardown); } catch (e) { this.handleInitializationError(e); } } 复制代码
在initialize中,重点关注asyncRun
中的逻辑,也就是:
await ExceptionsZone.asyncRun(async () => { await dependenciesScanner.scan(module); await instanceLoader.createInstancesOfDependencies(); dependenciesScanner.applyApplicationProviders(); }, teardown); 复制代码
这里面做了三件事:
首先,我们来看一下scan
的部分:
scan
scan部分的代码位于packages/core/scanner.ts
:
// packages/core/scanner.ts export class DependenciesScanner { public async scan(module: Type<any>) { await this.registerCoreModule(); await this.scanForModules(module); await this.scanModulesForDependencies(); this.addScopedEnhancersMetadata(); this.container.bindGlobalScope(); } } 复制代码
可以看到, scan里面做了一些事情,我们先从rigisterCoreModule
看起:
public async registerCoreModule() { const module = this.container.createCoreModule(); const instance = await this.scanForModules(module); this.container.registerCoreModuleRef(instance); } 复制代码
rigisterCoreModule
rigisterCoreModule的逻辑并不复杂,首先调用container.crateCoreModule
创建Module,通过scanForModules
实例化Module,最后再通过container.registerCorModule
注册:
关于NestContainer
NestContainer位于packages/core/injector/container.ts
,是一个用于实现依赖注入机制的Ioc容器。在这里,我们从Module
层面首先接触到了该容器。
crateCoreModule的返回
首先通过NestContainer
创建的coreModule
返回的是一个典型的Nest Dynamic Module,它的值如下:
module = { exports: [ExternalContextCreator, ModulesContainer, HttpAdapterHost], module: InternalCoreModule, providers: [{ provide: ExternalContextCreator, useValue: ExternalContextCreator.fromContainer(this), }, { provide: ModulesContainer, useValue: this.modules, }, { provide: HttpAdapterHost, useValue: this.internalProvidersStorage.httpAdapterHost, }] } 复制代码
可以看到 coreModule的provider是通过value
的形式注入。
scanForModules核心逻辑
public async scanForModules( module: ForwardReference | Type<unknown> | DynamicModule, scope: Type<unknown>[] = [], ctxRegistry: (ForwardReference | DynamicModule | Type<unknown>)[] = [], ): Promise<Module> { const moduleInstance = await this.insertModule(module, scope); ctxRegistry.push(module); if (this.isForwardReference(module)) { module = (module as ForwardReference).forwardRef(); } const modules = !this.isDynamicModule(module as Type<any> | DynamicModule) ? this.reflectMetadata(module as Type<any>, MODULE_METADATA.IMPORTS) : [ ...this.reflectMetadata( (module as DynamicModule).module, MODULE_METADATA.IMPORTS, ), ...((module as DynamicModule).imports || []), ]; for (const [index, innerModule] of modules.entries()) { // In case of a circular dependency (ES module system), JavaScript will resolve the type to `undefined`. if (innerModule === undefined) { throw new UndefinedModuleException(module, index, scope); } if (!innerModule) { throw new InvalidModuleException(module, index, scope); } if (ctxRegistry.includes(innerModule)) { continue; } await this.scanForModules( innerModule, [].concat(scope, module), ctxRegistry, ); } return moduleInstance; } 复制代码
在scanForModules
中,首先调用了insertModule
方法创建了一个moduleInstance,该方法实际上是调用了container.addModule
,让我们来看看addModule
做了什么:
public async addModule( metatype: Type<any> | DynamicModule | Promise<DynamicModule>, scope: Type<any>[], ): Promise<Module> { // In DependenciesScanner#scanForModules we already check for undefined or invalid modules // We sill need to catch the edge-case of `forwardRef(() => undefined)` if (!metatype) { throw new UndefinedForwardRefException(scope); } const { type, dynamicMetadata, token } = await this.moduleCompiler.compile( metatype, ); if (this.modules.has(token)) { return; } const moduleRef = new Module(type, this); this.modules.set(token, moduleRef); await this.addDynamicMetadata( token, dynamicMetadata, [].concat(scope, type), ); if (this.isGlobalModule(type, dynamicMetadata)) { this.addGlobalModule(moduleRef); } return moduleRef; } 复制代码
首先,addModule调用了moduleCompiler.compile
获取到token,该文件位于packages/core/injector/compiler.ts
:
// core/injector/compiler.ts export class ModuleCompiler { public async compile( metatype: Type<any> | DynamicModule | Promise<DynamicModule>, ): Promise<ModuleFactory> { const { type, dynamicMetadata } = await this.extractMetadata(metatype); const token = this.moduleTokenFactory.create(type, dynamicMetadata); return { type, dynamicMetadata, token }; } } 复制代码
实际上是通过moduleTokenFactory.create
创建了一个token,该文件位于packages/core/injector/module-token-factory.ts
:
// packages/core/injector/module-token-factory.ts export class ModuleTokenFactory { private readonly moduleIdsCache = new WeakMap<Type<unknown>, string>(); public create( metatype: Type<unknown>, dynamicModuleMetadata?: Partial<DynamicModule> | undefined, ): string { const moduleId = this.getModuleId(metatype); const opaqueToken = { id: moduleId, module: this.getModuleName(metatype), dynamic: this.getDynamicMetadataToken(dynamicModuleMetadata), }; return hash(opaqueToken, { ignoreUnknown: true }); } } 复制代码
Module token生成的逻辑如下:
接下来,addModule缓存token,并创建了一个Module对象,Module的代码在packages/core/injector/module.ts
中:
// packages/core/injector/module.ts export class Module { constructor( private readonly _metatype: Type<any>, private readonly container: NestContainer, ) { this.addCoreProviders(); this._id = randomStringGenerator(); } } 复制代码
Module对象的创建过程中,主要执行了addCoreProviders
方法:
public addCoreProviders() { this.addModuleAsProvider(); this.addModuleRef(); this.addApplicationConfig(); } 复制代码
这个方法的核心思想是Providers创建instanceWrapper,存储在Module的_providers数组中:
public addModuleRef() { const moduleRef = this.createModuleReferenceType(); this._providers.set( ModuleRef.name, new InstanceWrapper({ name: ModuleRef.name, metatype: ModuleRef as any, isResolved: true, instance: new moduleRef(), host: this, }), ); } public addModuleAsProvider() { this._providers.set( this._metatype.name, new InstanceWrapper({ name: this._metatype.name, metatype: this._metatype, isResolved: false, instance: null, host: this, }), ); } public addApplicationConfig() { this._providers.set( ApplicationConfig.name, new InstanceWrapper({ name: ApplicationConfig.name, isResolved: true, instance: this.container.applicationConfig, host: this, }), ); } 复制代码
至此,一个Module的创建就算完成了,scanForModules
的工作就是对Module的imports
字段做一个深度优先遍历,把所依赖的Module全部添加创建完成:
scanModulesForDependencies
接下来分析下scanModulesForDependencies的执行过程:
public async scanModulesForDependencies() { const modules = this.container.getModules(); for (const [token, { metatype }] of modules) { await this.reflectImports(metatype, token, metatype.name); this.reflectProviders(metatype, token); this.reflectControllers(metatype, token); this.reflectExports(metatype, token); } this.calculateModulesDistance(modules); } 复制代码
这个阶段主要做了两件事情,一个是将Module相关的依赖,如imports
、providers
、controllers
、exports
等,分别解析并添加,类似的方法在Module中定义:
接着执行calculateModulesDistance
方法:
public async calculateModulesDistance(modules: ModulesContainer) { const modulesGenerator = modules.values(); const rootModule = modulesGenerator.next().value as Module; const modulesStack = [rootModule]; const calculateDistance = (moduleRef: Module, distance = 1) => { if (modulesStack.includes(moduleRef)) { return; } modulesStack.push(moduleRef); const moduleImports = rootModule.relatedModules; moduleImports.forEach(module => { module.distance = distance; calculateDistance(module, distance + 1); }); }; calculateDistance(rootModule); } 复制代码
目前该方法看起来是有bug,在于rootModule被判断在ModuleStack中,直接return,而calculateDistance方法体一直得不到执行?
至此,scan阶段就告一段落。
createInstancesOfDependencies
createInstancesOfDependencies位于/packages/core/injector/instance-loader.ts
中:
// packages/core/injector/instance-loader.ts export class InstanceLoader { public async createInstancesOfDependencies() { const modules = this.container.getModules(); this.createPrototypes(modules); await this.createInstances(modules); } } 复制代码
该方法主要做了两件事情,首先为Modules创建instance的prototype,实现一个基于instanceWrapper的继承关系,其次去创建类的实例,根据Scan的存储结构逐步实例化,这一步也是实现依赖注入的关键逻辑。
createPrototypes
的关键逻辑主要如下(位于packages/core/injector/injector.ts
):
// packages/core/injector/injector.ts public loadPrototype<T>( { name }: InstanceWrapper<T>, collection: Map<string, InstanceWrapper<T>>, contextId = STATIC_CONTEXT, ) { if (!collection) { return; } const target = collection.get(name); const instance = target.createPrototype(contextId); if (instance) { const wrapper = new InstanceWrapper({ ...target, instance, }); collection.set(name, wrapper); } 复制代码
该方法调用intanceWrapper
的createPrototype
来建立一个简单的继承关系:
public createPrototype(contextId: ContextId) { const host = this.getInstanceByContextId(contextId); if (!this.isNewable() || host.isResolved) { return; } return Object.create(this.metatype.prototype); } 复制代码
接下来是创建实例的过程:
private async createInstances(modules: Map<string, Module>) { await Promise.all( [...modules.values()].map(async module => { await this.createInstancesOfProviders(module); await this.createInstancesOfInjectables(module); await this.createInstancesOfControllers(module); const { name } = module.metatype; this.isModuleWhitelisted(name) && this.logger.log(MODULE_INIT_MESSAGE`${name}`); }), ); } 复制代码
不论是Providers、Injectables、Controllers,最终都需要调用loadInstance
方法来完成这一过程:
public async loadInstance<T>( wrapper: InstanceWrapper<T>, collection: Map<string, InstanceWrapper>, moduleRef: Module, contextId = STATIC_CONTEXT, inquirer?: InstanceWrapper, ) { const inquirerId = this.getInquirerId(inquirer); const instanceHost = wrapper.getInstanceByContextId(contextId, inquirerId); if (instanceHost.isPending) { return instanceHost.donePromise; } const done = this.applyDoneHook(instanceHost); const { name, inject } = wrapper; const targetWrapper = collection.get(name); if (isUndefined(targetWrapper)) { throw new RuntimeException(); } if (instanceHost.isResolved) { return done(); } const callback = async (instances: unknown[]) => { const properties = await this.resolveProperties( wrapper, moduleRef, inject, contextId, wrapper, inquirer, ); const instance = await this.instantiateClass( instances, wrapper, targetWrapper, contextId, inquirer, ); this.applyProperties(instance, properties); done(); }; await this.resolveConstructorParams<T>( wrapper, moduleRef, inject, callback, contextId, wrapper, inquirer, ); } 复制代码
我们先来看一下resolveConstructorParams
的过程:
public async resolveConstructorParams<T>( wrapper: InstanceWrapper<T>, moduleRef: Module, inject: InjectorDependency[], callback: (args: unknown[]) => void, contextId = STATIC_CONTEXT, inquirer?: InstanceWrapper, parentInquirer?: InstanceWrapper, ) { const inquirerId = this.getInquirerId(inquirer); const metadata = wrapper.getCtorMetadata(); if (metadata && contextId !== STATIC_CONTEXT) { const deps = await this.loadCtorMetadata( metadata, contextId, inquirer, parentInquirer, ); return callback(deps); } const dependencies = isNil(inject) ? this.reflectConstructorParams(wrapper.metatype as Type<any>) : inject; const optionalDependenciesIds = isNil(inject) ? this.reflectOptionalParams(wrapper.metatype as Type<any>) : []; let isResolved = true; const resolveParam = async (param: unknown, index: number) => { try { if (this.isInquirer(param, parentInquirer)) { return parentInquirer && parentInquirer.instance; } const paramWrapper = await this.resolveSingleParam<T>( wrapper, param, { index, dependencies }, moduleRef, contextId, inquirer, index, ); const instanceHost = paramWrapper.getInstanceByContextId( contextId, inquirerId, ); if (!instanceHost.isResolved && !paramWrapper.forwardRef) { isResolved = false; } return instanceHost && instanceHost.instance; } catch (err) { const isOptional = optionalDependenciesIds.includes(index); if (!isOptional) { throw err; } return undefined; } }; const instances = await Promise.all(dependencies.map(resolveParam)); isResolved && (await callback(instances)); } 复制代码
该方法是解析出constructor里面的参数,利用Reflect.metadata('design:paramtypes')
可以非常便捷地拿到参数,针对参数进行逐步的递归遍历分析,通过resolveComponentHost
方法来进行递归调用:
public async resolveComponentHost<T>( moduleRef: Module, instanceWrapper: InstanceWrapper<T>, contextId = STATIC_CONTEXT, inquirer?: InstanceWrapper, ): Promise<InstanceWrapper> { const inquirerId = this.getInquirerId(inquirer); const instanceHost = instanceWrapper.getInstanceByContextId( contextId, inquirerId, ); if (!instanceHost.isResolved && !instanceWrapper.forwardRef) { await this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer); } else if ( !instanceHost.isResolved && instanceWrapper.forwardRef && (contextId !== STATIC_CONTEXT || !!inquirerId) ) { /** * When circular dependency has been detected between * either request/transient providers, we have to asynchronously * resolve instance host for a specific contextId or inquirer, to ensure * that eventual lazily created instance will be merged with the prototype * instantiated beforehand. */ instanceHost.donePromise && instanceHost.donePromise.then(() => this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer), ); } if (instanceWrapper.async) { const host = instanceWrapper.getInstanceByContextId( contextId, inquirerId, ); host.instance = await host.instance; instanceWrapper.setInstanceByContextId(contextId, host, inquirerId); } return instanceWrapper; } 复制代码
当参数加载解析完成后,统一做实例化的操作:
public async instantiateClass<T = any>( instances: any[], wrapper: InstanceWrapper, targetMetatype: InstanceWrapper, contextId = STATIC_CONTEXT, inquirer?: InstanceWrapper, ): Promise<T> { const { metatype, inject } = wrapper; const inquirerId = this.getInquirerId(inquirer); const instanceHost = targetMetatype.getInstanceByContextId( contextId, inquirerId, ); const isStatic = wrapper.isStatic(contextId, inquirer); const isInRequestScope = wrapper.isInRequestScope(contextId, inquirer); const isLazyTransient = wrapper.isLazyTransient(contextId, inquirer); const isExplicitlyRequested = wrapper.isExplicitlyRequested( contextId, inquirer, ); const isInContext = isStatic || isInRequestScope || isLazyTransient || isExplicitlyRequested; if (isNil(inject) && isInContext) { instanceHost.instance = wrapper.forwardRef ? Object.assign( instanceHost.instance, new (metatype as Type<any>)(...instances), ) : new (metatype as Type<any>)(...instances); } else if (isInContext) { const factoryReturnValue = ((targetMetatype.metatype as any) as Function)( ...instances, ); instanceHost.instance = await factoryReturnValue; } instanceHost.isResolved = true; return instanceHost.instance; } 复制代码
实例化后的结果保存在instanceHost中,至此,实例化的工作结束。
app的生成
再回到create
的逻辑,当一切初始化完毕,返回一个NestApplication
的实例,也就是app:
public async create<T extends INestApplication = INestApplication>( module: any, serverOrOptions?: AbstractHttpAdapter | NestApplicationOptions, options?: NestApplicationOptions, ): Promise<T> { const [httpServer, appOptions] = this.isHttpServer(serverOrOptions) ? [serverOrOptions, options] : [this.createHttpAdapter(), serverOrOptions]; const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); this.setAbortOnError(serverOrOptions, options); this.applyLogger(appOptions); await this.initialize(module, container, applicationConfig, httpServer); const instance = new NestApplication( container, httpServer, applicationConfig, appOptions, ); const target = this.createNestInstance(instance); return this.createAdapterProxy<T>(target, httpServer); } 复制代码
NestApplication
位于packages/core/nest-application.ts
中,包含了与httpServer(Adapter)的关联,初始化RoutesResolver
等一系列逻辑,这些我们后面再分析:
// packages/core/nest-application.ts export class NestApplication extends NestApplicationContext implements INestApplication { constructor( container: NestContainer, private readonly httpAdapter: HttpServer, private readonly config: ApplicationConfig, private readonly appOptions: NestApplicationOptions = {}, ) { super(container); this.selectContextModule(); this.registerHttpServer(); this.routesResolver = new RoutesResolver( this.container, this.config, this.injector, ); } } 复制代码
依赖注入的实现总结
回顾一下Nest.js的依赖注入实现思路,主要分为三个大步骤(两个阶段):
【Scan阶段】启动程序,通过APPModule,在Scanner模块逐步寻找相关Module,构造Module依赖树
【Scan阶段】在构造Module的同时,为providers、controllers、middwares、injectables等创建instanceWrapper实例
【instance阶段】实例化过程,分析contributor构造传参,根据依赖关系从叶子节点开始逐步递归进行实例化,存储在instanceWrapper的values集合中(非DEFAULT的Scope是有多个实例的)
Scan阶段
Modules看上去的依赖关系如下:
它主要注册在Container中:
private readonly modules = new ModulesContainer(); 复制代码
而ModuleContainer实际上是Module的一个Map:
export class ModulesContainer extends Map<string, Module> {} 复制代码
Module本身的数据结构中,则存储了在Module范围内的所有子组件集合:
private readonly _imports = new Set<Module>(); private readonly _providers = new Map<any, InstanceWrapper<Injectable>>(); private readonly _injectables = new Map<any, InstanceWrapper<Injectable>>(); private readonly _middlewares = new Map<any, InstanceWrapper<Injectable>>(); private readonly _controllers = new Map< 复制代码
每一个Map集合中都存储了instanceWrapper,在该Wrapper上去挂载最后的实例。
以上就是Scanner阶段的核心。
instance阶段
instance阶段的核心是——怎样拿到类的构造函数参数?
一旦确定了构造函数参数,我们就可以根据构造函数参数,来找到对应的Provider,再找出更深层次的Provider依赖,经过一层深度优先遍历,找到叶子节点的Provider,初始化整个树(实际是以数组结构存储)
获取构造函数,主要是依靠Reflect
机制,获取到Metadata
,根据元数据拿到参数的index及value:
// 以下是一个injectable装饰的类 @injectable class Provider { constructor( private instance: Instance ) {} } Reflect.getMetadata('design:paramtypes', Provider) // 拿到[instance] 复制代码
接下来,通过深度优先遍历,逐步实例化,就是该阶段的核心思想。
当然在遍历的过程中,需要注意有循环依赖的情况,Nest.js如何处理呢?我们后面文章再单独介绍。
小结
本文介绍了Nest.js的启动过程,以及Nest.js实现依赖注入的整体原理,最后总结了依赖注入实现的核心思想。依赖注入是Nest.js的核心思想,也是学习Nest.js的必经之路,希望能够帮助到有需要的同学。
作者:孟健
链接:https://juejin.cn/post/6898685628042870797