前端搞懂Nest基本原理看这一篇就够啦!

1,029 阅读7分钟

前言

Nest框架是一个当下流行的,高效可扩展的Node框架。与Java中大名鼎鼎的Spring框架原理相似,Nest使用了控制反转(InverseOfControl,IoC)与依赖注入(Dependency Injection,DI)原则,对复杂工程进行解耦,提倡切面编程。

一、什么是IoC,DI与AOP?

1、控制反转IoC

在传统开发中,类的实例化一般是由开发者完成的,开发者可以根据自身业务需求来实例化自己需要的类,使用其功能。这时,实例化的控制权在开发者手里,但是带来的问题是开发者可能实例化了过多的实例,并且管理较为混乱。相反,控制反转就是将类实例化的权利上交给第三方框架,让框架来帮助开发者实例化对象并进行管理,开发者在需要使用时只需要向IoC容器"伸手"要就行了,不再需要关注类之间的依赖关系。

IoC.png

2、依赖注入DI

当使用IoC的思想进行开发时,开发者是不需要手动实例化当前类中的依赖的,IoC容器帮开发者实例化当前类中的依赖便称为依赖注入。

3、面向切面编程(Aspect Oriented Programming,AOP)

面向切面编程是面向对象编程(OOP)的一种发展。在面向对象编程中,我们可以通过继承来获取父类中的属性和方法,再进行功能扩展。但OOP也会出现一些耦合度高的问题,而AOP的出现就是为了解决高耦合度的问题,如:Dog和Cat类都继承于Animal类,拥有了eat和run方法。

AOP.png

但有时候在分别对eat和run方法进行处理时很可能会出现一些重合的逻辑代码:

class Animal {
    eat() {
        // 性能监控代码
        console.log("I can eat...");
        // 业务逻辑代码
        console.log("I can eat...");
        // 性能监控代码
        console.log("I can eat...");
    }
    run() {
        // 性能监控代码
        console.log("I can run...");
        // 业务逻辑代码
        console.log("I can run...");
        
        // 性能监控代码
        console.log("I can run...");
    }
}

这些重复的逻辑代码称为:"横切逻辑代码",而AOP则负责将这些横切代码抽离出来,降低代码的耦合性。

AOP2.png

介绍了Nest框架使用的设计思想,接下来看看这些设计思想在Nest中的实现。不过在这之前,我们需要先具备一些相关知识储备。

二、Reflect Metadata

元数据反射(Reflect Metadata)是ES7的提案,用于存储,读取对象的元数据。

首先介绍反射的概念,反射是指在运行时获取对象的相关数据(属性和方法等)。由于TS类型存在于静态编译,而运行时怎么获取相关依赖的类型信息呢,原因就在于依赖反射的动态类型反推导。

使用Reflect Metadata

要使用Reflect Metadata需要安装一个polifill包

npm i reflect-metadata --save

定义元数据

Reflect.defineMetadata(metadataKey, data, target)
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)

获取元数据

Reflect.getMetadata(metadataKey, target)
Reflect.getMetadata(metadataKey, target, propertyKey);

这里的target可以是对象也可以是class或者是对象的某个属性,同时还支持以装饰器的方式使用

@Reflect.metadata(metadataKey, metadataValue)
class C {
  @Reflect.metadata(metadataKey, metadataValue)
  method() {
  }
}

我们来看看Reflect.getMetadata在Nest中的简单使用:

import 'reflect-metadata';
class A {
  sayHi() {
    console.log('hi');
  }
}
class B {
  sayHello() {
    console.log('hello');
  }
}
function Module(metadata) {
  return (target) => {
    for (const property in metadata) {
      if (metadata.hasOwnProperty(property)) {
        Reflect.defineMetadata(property, metadata[property], target);
      }
    }
  };
}
@Module({
  controllers: [B],
  providers: [A],
})
class C {}
const providers = Reflect.getMetadata('providers', C);
const controllers = Reflect.getMetadata('controllers', C);
console.log(providers, controllers); // [ [class A] ] [ [class B] ]

通过Reflect拿到了C的controllers和providers。

使用反射推荐搭配TS一期使用,并在tsconfig.json文件里配置emitDecoratorMetadata为true。

配置该选项后,TS在编译时会向使用装饰器的对象里注入3组元数据:

  • design:type:成员类型
  • design:paramtypes:成员所有参数类型
  • design:returntype:成员返回类型

image.png

image.png

可以看到TS向相关对象里注入了相关的元数据,这样就可以利用这些元数据来完成依赖注入了。

接下来,我们来看看Nest中的源码具体是怎么做到IoC的。

三、Nest的源码实现

1、NestApplication的创建逻辑

NestApplication是整个Nest服务的代理对象类,在入口文件main.ts,创建了这个类的实例:

const app = await NestFactory.create(AppModule);

我们来看一下这个create方法的具体实现:

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();
    // 1. 实例化IoC容器,这个容器就是用来存放实例对象的地方
    const container = new NestContainer(applicationConfig); 
    this.setAbortOnError(serverOrOptions, options);
    this.registerLoggerConfiguration(appOptions);
	// 2. 初始化逻辑,这里实现了依赖注入
    await this.initialize(module, container, applicationConfig, httpServer); 
		
	// 3. 实例化NestApplication类
    const instance = new NestApplication(     
      container,
      httpServer,
      applicationConfig,
      appOptions,
    );
    const target = this.createNestInstance(instance);
    // 4. 生成一个Proxy代理对象,将对NestApplication实例上部分属性的访问代理到httpServer
    return this.createAdapterProxy<T>(target, httpServer); 
  }

上面看到实例化了一个IoC容器,这里便存放了相关依赖的实例对象,来看看里面的实现:

2、NestContainer,IoC容器

image.png

image.png

image.png

IoC容器里的私有属性modules保存了当前的Module对象,它所依赖的provider和controller等都存放在对应的module对象里。

可以看见modules属性为Map的一个子类。

image.png

了解了IoC容器后,我们来看看依赖注入过程:

3、依赖注入

initialize方法

首先我们来看NestFactory.create方法里面的initialize方法。

  private async initialize(
    module: any,
    container: NestContainer,
    config = new ApplicationConfig(),
    httpServer: HttpServer = null,
  ) {
    // 1. 实例加载器
    const instanceLoader = new InstanceLoader(container);  
    const metadataScanner = new MetadataScanner();				 
    // 2. 依赖扫描器
    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 () => {
          // 3. 扫描依赖
          await dependenciesScanner.scan(module); 
          // 4. 生成依赖的实例
          await instanceLoader.createInstancesOfDependencies(); 
          dependenciesScanner.applyApplicationProviders();
        },
        teardown,
        this.autoFlushLogs,
      );
    } catch (e) {
      this.handleInitializationError(e);
    }
  }

initialize方法主要干了三件事:

  1. 创建Container的实例加载器
  2. 进行依赖扫描
  3. 生成依赖实例

将container作为参数传给实例加载器,之后可以为container相关的依赖创建实例,具体的创建过程我们之后再看,现在先看看依赖扫描器是如何进行依赖扫描的。

依赖扫描

代码中调用了dependenciesScanner的scan方法,下面是scan方法的内容:

   public async scan(module: Type<any>) {
     // 1. 把一些框架内建的module添加到IoC容器中
     await this.registerCoreModule(); 
     // 2. 把传入的module添加到IoC容器中
     await this.scanForModules(module); 
     // 3. 扫描当前IoC容器中所有module的依赖
     await this.scanModulesForDependencies(); 
     this.calculateModulesDistance();
   
     this.addScopedEnhancersMetadata();
     this.container.bindGlobalScope();
   }

scan方法中对module中的依赖进行了扫描,其主要过程在this.scanModulesForDependencies()方法中实现:

async scanModulesForDependencies(modules = this.container.getModules()) {
        for (const [token, { metatype }] of modules) {
            // 依次获取当前module的imports, providers, controllers, exports
            await this.reflectImports(metatype, token, metatype.name);
            this.reflectProviders(metatype, token);
            this.reflectControllers(metatype, token);
            this.reflectExports(metatype, token);
        }
    }

scanModulesForDependencies方法获取了当前module的imports, providers, controllers, exports这些元数据,以providers为例我们可以看到reflectProviders方法调用了this.reflectMetadata方法来获取providers元数据。

reflectProviders(module, token) {
    const providers = [
        ...this.reflectMetadata(constants_1.MODULE_METADATA.PROVIDERS, module),
        ...this.container.getDynamicMetadataByToken(token, constants_1.MODULE_METADATA.PROVIDERS),
    ];
    providers.forEach(provider => {
        this.insertProvider(provider, token);
        this.reflectDynamicMetadata(provider, token);
    });
}

让我们看看this.reflectMetadata方法内部是什么。

reflectMetadata(metadataKey, metatype) {
    return Reflect.getMetadata(metadataKey, metatype) || [];
}

我们看到它使用的就是Reflect.getMetadata方法来获取providers的元数据,这里的元数据正是使用@Module装饰器传入的元数据,获取到providers元数据后调用了this.insertProvider(provider, token)方法将providers元数据添加到了IoC Container中。

完成了Module的依赖扫描,接下来就是要实例化这些依赖了。这里就用到了我们之前的实例加载器instanceLoader。

实例化依赖

在initialize方法中调用了instanceLoader.createInstancesOfDependencies()方法来实例化依赖项,下面是createInstancesOfDependencies()方法的实现:

async createInstancesOfDependencies(modules = this.container.getModules()) {
    this.createPrototypes(modules);
    await this.createInstances(modules);
}

这里的代码很简洁,主要就是调用了this.createInstances(modules)方法。

async createInstances(modules) {
    await Promise.all([...modules.values()].map(async (moduleRef) => {
        await this.createInstancesOfProviders(moduleRef);
        await this.createInstancesOfInjectables(moduleRef);
        await this.createInstancesOfControllers(moduleRef);
        const { name } = moduleRef.metatype;
        this.isModuleWhitelisted(name) &&
            this.logger.log((0, messages_1.MODULE_INIT_MESSAGE) `${name}`);
    }));
}

在this.createInstances(modules)方法,可以看到是按照Providers,Injectables,Controllers的顺序进行实例化。

以Providers为例,我们看看实例化的过程:

async createInstancesOfProviders(moduleRef) {
    const { providers } = moduleRef;
    const wrappers = [...providers.values()];
    await Promise.all(wrappers.map(item => this.injector.loadProvider(item, moduleRef)));
}

这里又通过调用Injector类来进行实例化。

这个类里面的具体代码我们不再关注了,我们只看一些和实例化有关的核心代码。

在Injector类的loadInstance方法里调用了this.resolveConstructorParams()方法:

image.png

这个方法是对构造函数中的参数进行解析,创建实例,然后将创建好的实例传给了回调函数callback中,执行this.instantiateClass函数实例化Provider对象。

这里需要关注的是this.resolveConstructorParams()方法中依赖的获取与实例化:

async resolveConstructorParams(wrapper, moduleRef, inject, callback, contextId = constants_2.STATIC_CONTEXT, inquirer, parentInquirer) {
    let inquirerId = this.getInquirerId(inquirer);
    const metadata = wrapper.getCtorMetadata();
    if (metadata && contextId !== constants_2.STATIC_CONTEXT) {
        const deps = await this.loadCtorMetadata(metadata, contextId, inquirer, parentInquirer);
        return callback(deps);
    }
    const isFactoryProvider = !(0, shared_utils_1.isNil)(inject);
    // 获取依赖项
    const [dependencies, optionalDependenciesIds] = isFactoryProvider
        ? this.getFactoryProviderDependencies(wrapper)
        : this.getClassDependencies(wrapper);
    let isResolved = true;
    const resolveParam = async (param, index) => {
        try {
            if (this.isInquirer(param, parentInquirer)) {
                return parentInquirer && parentInquirer.instance;
            }
            if ((inquirer === null || inquirer === void 0 ? void 0 : inquirer.isTransient) && parentInquirer) {
                inquirer = parentInquirer;
                inquirerId = this.getInquirerId(parentInquirer);
            }
            const paramWrapper = await this.resolveSingleParam(wrapper, param, { index, dependencies }, moduleRef, contextId, inquirer, index);
            // 将依赖实例化
            const instanceHost = paramWrapper.getInstanceByContextId(this.getContextId(contextId, paramWrapper), inquirerId);
            if (!instanceHost.isResolved && !paramWrapper.forwardRef) {
                isResolved = false;
            }
            return instanceHost === null || instanceHost === void 0 ? void 0 : instanceHost.instance;
        }
        catch (err) {
            const isOptional = optionalDependenciesIds.includes(index);
            if (!isOptional) {
                throw err;
            }
            return undefined;
        }
    };
    const instances = await Promise.all(dependencies.map(resolveParam));
    // 传入callback
    isResolved && (await callback(instances));
}

将实例化的依赖传入了回调函数callback,回调函数中调用this.instantiateClass函数。

在this.instantiateClass函数中将这些依赖项实例,作为参数传入Provider构造函数中,实例化Provider返回。

image.png

4、拦截器(Interceptor)

Interceptor装饰器的使用

这里我们来介绍一下Nest中拦截器创建的过程。在Nest中我们通过@UseInterceptors拦截器给当前Controller或者给某个路由添加拦截器:

@UseInterceptors(SomeInterceptor)
@Controller('add')
export class Controller {
  ...
}
@UseInterceptors(SomeInterceptor)
async list(
    @Req() req: Request,
): Promise<Response> {
   ...
}

那就先看看UseInterceptors这个装饰器到底干了什么。

export function UseInterceptors(
  ...interceptors: (NestInterceptor | Function)[]
): MethodDecorator & ClassDecorator {
  return (
    target: any,
    key?: string | symbol,
    descriptor?: TypedPropertyDescriptor<any>,
  ) => {
    const isInterceptorValid = <T extends Function | Record<string, any>>(
      interceptor: T,
    ) =>
      interceptor &&
      (isFunction(interceptor) ||
        isFunction((interceptor as Record<string, any>).intercept));
    if (descriptor) {
      validateEach(
        target.constructor,
        interceptors,
        isInterceptorValid,
        '@UseInterceptors',
        'interceptor',
      );
      extendArrayMetadata(
        INTERCEPTORS_METADATA,
        interceptors,
        descriptor.value,
      );
      return descriptor;
    }
    validateEach(
      target,
      interceptors,
      isInterceptorValid,
      '@UseInterceptors',
      'interceptor',
    );
    // 核心代码
    extendArrayMetadata(INTERCEPTORS_METADATA, interceptors, target);
    return target;
  };

这里的核心代码就是利用Reflect给target类或者target方法注入了INTERCEPTORS_METADATA("interceptors")及interceptors:

export function extendArrayMetadata<T extends Array<unknown>>(
  key: string,
  metadata: T,
  target: Function,
) {
    const previousValue = Reflect.getMetadata(key, target) || [];
    const value = [...previousValue, ...metadata];
    Reflect.defineMetadata(key, value, target);
}

这里给target类或者target方法注入了拦截器元数据后,在之后的Controller实例的创建过程中可以拿到拦截器进行相关操作了。

那么Nest具体是如何实现拦截器的获取与应用呢,这里Nest使用了生产消费模式,定义了两个类:InterceptorsContextCreator与InterceptorsConsumer。

Interceptor的创建

Nest使用RouterExecutionContext类来创建Controller的。

在这个类里面InterceptorsContextCreator类负责创建Interceptor实例以及其所需的context。

InterceptorsConsumer负责使用当前类的Interceptor。

export class RouterExecutionContext {
  private readonly handlerMetadataStorage = new HandlerMetadataStorage();
  private readonly contextUtils = new ContextUtils();
  private readonly responseController: RouterResponseController;
  constructor(
    private readonly paramsFactory: IRouteParamsFactory,
    private readonly pipesContextCreator: PipesContextCreator,
    private readonly pipesConsumer: PipesConsumer,
    private readonly guardsContextCreator: GuardsContextCreator,
    private readonly guardsConsumer: GuardsConsumer,
    private readonly interceptorsContextCreator: InterceptorsContextCreator, // 创建拦截器
    private readonly interceptorsConsumer: InterceptorsConsumer, // 使用拦截器
    readonly applicationRef: HttpServer,
  ) {
    this.responseController = new RouterResponseController(applicationRef);
  }
  public create(
    instance: Controller,
    callback: (...args: any[]) => unknown,
    methodName: string,
    moduleKey: string,
    requestMethod: RequestMethod,
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ) {
    const contextType: ContextType = 'http';
    const {
      argsLength,
      fnHandleResponse,
      paramtypes,
      getParamsMetadata,
      httpStatusCode,
      responseHeaders,
      hasCustomHeaders,
    } = this.getMetadata(
      instance,
      callback,
      methodName,
      moduleKey,
      requestMethod,
      contextType,
    );
    const paramsOptions = this.contextUtils.mergeParamsMetatypes(
      getParamsMetadata(moduleKey, contextId, inquirerId),
      paramtypes,
    );
    const pipes = this.pipesContextCreator.create(
      instance,
      callback,
      moduleKey,
      contextId,
      inquirerId,
    );
    const guards = this.guardsContextCreator.create(
      instance,
      callback,
      moduleKey,
      contextId,
      inquirerId,
    );
    // 创建当前类的拦截器实例
    const interceptors = this.interceptorsContextCreator.create(
      instance,
      callback,
      moduleKey,
      contextId,
      inquirerId,
    );
    const fnCanActivate = this.createGuardsFn(
      guards,
      instance,
      callback,
      contextType,
    );
    const fnApplyPipes = this.createPipesFn(pipes, paramsOptions);
    const handler =
      <TRequest, TResponse>(
        args: any[],
        req: TRequest,
        res: TResponse,
        next: Function,
      ) =>
      async () => {
        fnApplyPipes && (await fnApplyPipes(args, req, res, next));
        return callback.apply(instance, args);
      };
    return async <TRequest, TResponse>(
      req: TRequest,
      res: TResponse,
      next: Function,
    ) => {
      const args = this.contextUtils.createNullArray(argsLength);
      fnCanActivate && (await fnCanActivate([req, res, next]));
      this.responseController.setStatus(res, httpStatusCode);
      hasCustomHeaders &&
        this.responseController.setHeaders(res, responseHeaders);
        
      // interceptorsConsumer应用当前类的拦截器
      const result = await this.interceptorsConsumer.intercept(
        interceptors,
        [req, res, next],
        instance,
        callback,
        handler(args, req, res, next),
        contextType,
      );
      await (fnHandleResponse as HandlerResponseBasicFn)(result, res, req);
    };
  }

知道了主要流程,我们去InterceptorsContextCreator类中看看具体如何创建拦截器实例的:

export class InterceptorsContextCreator extends ContextCreator {
  private moduleContext: string;
  constructor(
    private readonly container: NestContainer,
    private readonly config?: ApplicationConfig,
  ) {
    super();
  }
  public create(
    instance: Controller,
    callback: (...args: unknown[]) => unknown,
    module: string,
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ): NestInterceptor[] {
    this.moduleContext = module;
    return this.createContext(
      instance,
      callback,
      INTERCEPTORS_METADATA,
      contextId,
      inquirerId,
    );
  }
  public createConcreteContext<T extends any[], R extends any[]>(
    metadata: T,
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ): R {
    if (isEmpty(metadata)) {
      return [] as R;
    }
    return iterate(metadata)
      .filter(
        interceptor =>
          interceptor && (interceptor.name || interceptor.intercept),
      )
      .map(interceptor =>
        this.getInterceptorInstance(interceptor, contextId, inquirerId),
      )
      .filter(
        (interceptor: NestInterceptor) =>
          interceptor && isFunction(interceptor.intercept),
      )
      .toArray() as R;
  }
  public getInterceptorInstance(
    metatype: Function | NestInterceptor,
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ): NestInterceptor | null {
    const isObject = (metatype as NestInterceptor).intercept;
    if (isObject) {
      return metatype as NestInterceptor;
    }
    const instanceWrapper = this.getInstanceByMetatype(
      metatype as Type<unknown>,
    );
    if (!instanceWrapper) {
      return null;
    }
    const instanceHost = instanceWrapper.getInstanceByContextId(
      contextId,
      inquirerId,
    );
    return instanceHost && instanceHost.instance;
  }
  public getInstanceByMetatype(
    metatype: Type<unknown>,
  ): InstanceWrapper | undefined {
    if (!this.moduleContext) {
      return;
    }
    const collection = this.container.getModules();
    const moduleRef = collection.get(this.moduleContext);
    if (!moduleRef) {
      return;
    }
    return moduleRef.injectables.get(metatype);
  }
  public getGlobalMetadata<T extends unknown[]>(
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ): T {
    if (!this.config) {
      return [] as T;
    }
    const globalInterceptors = this.config.getGlobalInterceptors() as T;
    if (contextId === STATIC_CONTEXT && !inquirerId) {
      return globalInterceptors;
    }
    const scopedInterceptorWrappers =
      this.config.getGlobalRequestInterceptors() as InstanceWrapper[];
    const scopedInterceptors = iterate(scopedInterceptorWrappers)
      .map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
      .filter(host => !!host)
      .map(host => host.instance)
      .toArray();
    return globalInterceptors.concat(scopedInterceptors) as T;
  }
}

代码结构比较清楚,是通过调用create方法来创建实例,该方法中调用了父类ContextCreator中的createContext方法,我们来看看该方法:

public createContext<T extends unknown[] = any, R extends unknown[] = any>(
    instance: Controller,
    callback: (...args: any[]) => void,
    metadataKey: string,
    contextId = STATIC_CONTEXT,
    inquirerId?: string,
  ): R {
    // 获取全局Metadata
    const globalMetadata =
      this.getGlobalMetadata &&
      this.getGlobalMetadata<T>(contextId, inquirerId);
    // 获取类的Metadata
    const classMetadata = this.reflectClassMetadata<T>(instance, metadataKey);
    // 获取方法的Metadata
    const methodMetadata = this.reflectMethodMetadata<T>(callback, metadataKey);
    // 将全局的,类的,方法的Metadata传入createConcreteContext方法进行实例化
    return [
      ...this.createConcreteContext<T, R>(
        globalMetadata || ([] as T),
        contextId,
        inquirerId,
      ),
      ...this.createConcreteContext<T, R>(classMetadata, contextId, inquirerId),
      ...this.createConcreteContext<T, R>(
        methodMetadata,
        contextId,
        inquirerId,
      ),
    ] as R;
  }

可以看出该方法获取了全局的,类的,方法的Metadata并将其传入createConcreteContext方法进行实例化。

通过上面的代码可以清楚看到getInterceptorInstance方法就是用来实例化拦截器的。

该方法参数metatype接受两种参数Function与NestInterceptor,传入NestInterceptor类对应的是Function,另一种则是传入NestInterceptor的实例对象:

// Function类型
@UseInterceptors(SomeInterceptor)
fn() {}
// NestInterceptor类型
@UseInterceptors(new SomeInterceptor())
fn() {}

在getInterceptorInstance方法中,metatype若是NestInterceptor类型则直接返回该实例。

metatype若是Function类型,则调用getInstanceByMetatype方法获取实例,而getInstanceByMetatype方法是通过当前moduleRef.injectables获取。

这说明拦截器是保存在对应module的injectables里的,那是什么时候实例化并保存在moduleRef.injectables里的呢?

为了找到源头,我们顺藤摸瓜去找module注入injectable元数据的地方。

在依赖扫描DependenciesScanner的类中,我们找到了注入injectable元数据的地方:

public reflectControllers(module: Type<any>, token: string) {
    const controllers = [
      ...this.reflectMetadata(MODULE_METADATA.CONTROLLERS, module),
      ...this.container.getDynamicMetadataByToken(
        token,
        MODULE_METADATA.CONTROLLERS as 'controllers',
      ),
    ];
    controllers.forEach(item => {
      this.insertController(item, token);
      this.reflectDynamicMetadata(item, token);
    });
}
public reflectDynamicMetadata(obj: Type<Injectable>, token: string) {
    if (!obj || !obj.prototype) {
      return;
    }
    this.reflectInjectables(obj, token, GUARDS_METADATA);
    this.reflectInjectables(obj, token, INTERCEPTORS_METADATA);
    this.reflectInjectables(obj, token, EXCEPTION_FILTERS_METADATA);
    this.reflectInjectables(obj, token, PIPES_METADATA);
    this.reflectParamInjectables(obj, token, ROUTE_ARGS_METADATA);
}

可以看到在获取Controller的时候通过reflectDynamicMetadata方法将Guard,Interceptor,ExceptionFilter,Pipe以及ROUTE_ARGS作为Injectable类型的元数据注入了Controller中。

这样便可以在依赖注入时完成类的实例化,这样就完成了拦截器的创建。

Interceptor的使用(消费)

InterceptorsConsumer负责拦截器的使用:

export class InterceptorsConsumer {
    public async intercept<TContext extends string = ContextType>(
        interceptors: NestInterceptor[],
        args: unknown[],
        instance: Controller,
        callback: (...args: unknown[]) => unknown,
        next: () => Promise<unknown>,
        type?: TContext,
        ): Promise<unknown> {
        if (isEmpty(interceptors)) {
          return next();
        }
        const context = this.createContext(args, instance, callback);
        context.setType<TContext>(type);
        
        const start$ = defer(() => this.transformDeferred(next));
        const nextFn =
          (i = 0) =>
          async () => {
            if (i >= interceptors.length) {
              return start$;
            }
            const handler: CallHandler = {
              handle: () => fromPromise(nextFn(i + 1)()).pipe(mergeAll()),
            };
            return interceptors[i].intercept(context, handler);
          };
        return nextFn()();
    }
}

这里就是从interceptors数组里依次取出全局,类,方法拦截器来执行。

以上就是Nest中拦截器的的创建与使用的整个过程,值得注意的是如果给@UseInterceptors装饰器中传入的是类名,可以借助Nest完成拦截器的中的依赖注入,若传入的是一个NestInterceptor实例,则Nest不会帮你完成依赖注入,需要你手动实例化。