从Nest.js的入口main.ts开始分析:bootstrap
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)
的逻辑:缓存
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
中的逻辑,也就是:markdown
await ExceptionsZone.asyncRun(async () => {
await dependenciesScanner.scan(module);
await instanceLoader.createInstancesOfDependencies();
dependenciesScanner.applyApplicationProviders();
}, teardown);
复制代码
这里面作了三件事:数据结构
首先,咱们来看一下scan
的部分:app
scan部分的代码位于packages/core/scanner.ts
:dom
// 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
看起:async
public async registerCoreModule() {
const module = this.container.createCoreModule();
const instance = await this.scanForModules(module);
this.container.registerCoreModuleRef(instance);
}
复制代码
rigisterCoreModule的逻辑并不复杂,首先调用container.crateCoreModule
建立Module,经过scanForModules
实例化Module,最后再经过container.registerCorModule
注册:ide
NestContainer位于packages/core/injector/container.ts
,是一个用于实现依赖注入机制的Ioc容器。在这里,咱们从Module
层面首先接触到了该容器。函数
首先经过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
的形式注入。
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的执行过程:
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位于/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中,至此,实例化的工做结束。
再回到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的依赖注入实现思路,主要分为三个大步骤(两个阶段):
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阶段的核心是——怎样拿到类的构造函数参数?
一旦肯定了构造函数参数,咱们就能够根据构造函数参数,来找到对应的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的必经之路,但愿可以帮助到有须要的同窗。