NestJS 原理探究之——依赖注入(续)

简介: NestJS 原理探究之——依赖注入(续)

承接笔记:NestJS原理探究之——依赖注入

首先看一下温习一下Demo中的 IoC容器


展开查看源码

import {
  Provider,
  isClassProvider,
  ClassProvider,
  ValueProvider,
  FactoryProvider,
  isValueProvider,
  Token,
  InjectionToken
} from "./provider";
import { Type } from "./type";
import { isInjectable } from "./injectable";
import "reflect-metadata";
import { getInjectionToken } from "./inject";
type InjectableParam = Type<any>;
const REFLECT_PARAMS = "design:paramtypes";
export class Container {
  private providers = new Map<Token<any>, Provider<any>>();
  addProvider<T>(provider: Provider<T>) {
    this.assertInjectableIfClassProvider(provider);
    this.providers.set(provider.provide, provider);
  }
  inject<T>(type: Token<T>): T {
    let provider = this.providers.get(type);
    if (provider === undefined && !(type instanceof InjectionToken)) {
      provider = { provide: type, useClass: type };
      this.assertInjectableIfClassProvider(provider);
    }
    return this.injectWithProvider(type, provider);
  }
  private injectWithProvider<T>(type: Token<T>, provider: Provider<T>): T {
    if (provider === undefined) throw new Error(`No provider for type c194a9eg<!-- begin-inline-katex{this.getTokenName(type)}`);
    if (isClassProvider(provider)) return this.injectClass(provider as ClassProvider<T>);
    else if (isValueProvider(provider)) return this.injectValue(provider as ValueProvider<T>);
    return this.injectFactory(provider as FactoryProvider<T>);
  }
  private assertInjectableIfClassProvider<T>(provider: Provider<T>) {
    if (isClassProvider(provider) && !isInjectable(provider.useClass))
      throw new Error(`Cannot provide end-inline-katex-->{this.getTokenName(provider.provide)} using class c194a9eg<!-- begin-inline-katex{this.getTokenName(provider.useClass)}, end-inline-katex-->{this.getTokenName(provider.useClass)} isn't injectable`);
  }
  private injectClass<T>(classProvider: ClassProvider<T>): T {
    const target = classProvider.useClass;
    const params = this.getInjectedParams(target);
    return Reflect.construct(target, params);
  }
  private injectValue<T>(valueProvider: ValueProvider<T>): T {
    return valueProvider.useValue;
  }
  private injectFactory<T>(factoryProvider: FactoryProvider<T>): T {
    return factoryProvider.useFactory();
  }
  private getInjectedParams<T>(target: Type<T>) {
    const argTypes = Reflect.getMetadata(REFLECT_PARAMS, target) as (InjectableParam | undefined)[];
    if (argTypes === undefined) return [];
    return argTypes.map((argType, index) => {
      // 在遇到循环依赖时,reflect-metadata API会失效,返回undefined
      if (argType === undefined) throw new Error(`Injection error. Recursive dependency detected in constructor for type c194a9eg<!-- begin-inline-katex{target.name} with parameter at index end-inline-katex-->{index}`);
      const overrideToken = getInjectionToken(target, index);
      const actualToken = overrideToken === undefined ? argType : overrideToken;
      let provider = this.providers.get(actualToken);
      return this.injectWithProvider(actualToken, provider);
    });
  }
  private getTokenName<T>(token: Token<T>) {
    return token instanceof InjectionToken ? token.injectionIdentifier : token.name;
  }
}


还有使用方法:

展开查看源码

import { Container } from "./container";
import { Inject } from "./inject";
import { Injectable } from "./injectable";
import { InjectionToken } from "./provider";
const API_URL = new InjectionToken("apiUrl");
@Injectable()
class HttpClient {}
@Injectable()
class HttpService {
  constructor(
    private httpClient: HttpClient,
    @Inject(API_URL) private apiUrl: string
  ) {}
}
const container = new Container();
container.addProvider({
  provide: API_URL,
  useValue: "https://www.baidu.com/",
});
container.addProvider({ provide: HttpClient, useClass: HttpClient });
container.addProvider({ provide: HttpService, useClass: HttpService });
const httpService = container.inject(HttpService);
console.dir(httpService);


NestJS 中的 IoC容器 源码在 packages/core/injector/container.ts 文件中实现。


展开查看源码

import { DynamicModule, Provider } from '@nestjs/common';
import { GLOBAL_MODULE_METADATA } from '@nestjs/common/constants';
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
import { Type } from '@nestjs/common/interfaces/type.interface';
import { ApplicationConfig } from '../application-config';
import { CircularDependencyException } from '../errors/exceptions/circular-dependency.exception';
import { UndefinedForwardRefException } from '../errors/exceptions/undefined-forwardref.exception';
import { UnknownModuleException } from '../errors/exceptions/unknown-module.exception';
import { ExternalContextCreator } from '../helpers/external-context-creator';
import { HttpAdapterHost } from '../helpers/http-adapter-host';
import { REQUEST } from '../router/request/request-constants';
import { ModuleCompiler } from './compiler';
import { ContextId } from './instance-wrapper';
import { InternalCoreModule } from './internal-core-module';
import { InternalProvidersStorage } from './internal-providers-storage';
import { Module } from './module';
import { ModuleTokenFactory } from './module-token-factory';
import { ModulesContainer } from './modules-container';
export class NestContainer {
  private readonly globalModules = new Set<Module>();
  private readonly moduleTokenFactory = new ModuleTokenFactory();
  private readonly moduleCompiler = new ModuleCompiler(this.moduleTokenFactory);
  private readonly modules = new ModulesContainer();
  private readonly dynamicModulesMetadata = new Map<
    string,
    Partial<DynamicModule>
  >();
  private readonly internalProvidersStorage = new InternalProvidersStorage();
  private internalCoreModule: Module;
  constructor(
    private readonly _applicationConfig: ApplicationConfig = undefined,
  ) {}
  get applicationConfig(): ApplicationConfig | undefined {
    return this._applicationConfig;
  }
  public setHttpAdapter(httpAdapter: any) {
    this.internalProvidersStorage.httpAdapter = httpAdapter;
    if (!this.internalProvidersStorage.httpAdapterHost) {
      return;
    }
    const host = this.internalProvidersStorage.httpAdapterHost;
    host.httpAdapter = httpAdapter;
  }
  public getHttpAdapterRef() {
    return this.internalProvidersStorage.httpAdapter;
  }
  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;
  }
  public async addDynamicMetadata(
    token: string,
    dynamicModuleMetadata: Partial<DynamicModule>,
    scope: Type<any>[],
  ) {
    if (!dynamicModuleMetadata) {
      return;
    }
    this.dynamicModulesMetadata.set(token, dynamicModuleMetadata);
    const { imports } = dynamicModuleMetadata;
    await this.addDynamicModules(imports, scope);
  }
  public async addDynamicModules(modules: any[], scope: Type<any>[]) {
    if (!modules) {
      return;
    }
    await Promise.all(modules.map(module => this.addModule(module, scope)));
  }
  public isGlobalModule(
    metatype: Type<any>,
    dynamicMetadata?: Partial<DynamicModule>,
  ): boolean {
    if (dynamicMetadata && dynamicMetadata.global) {
      return true;
    }
    return !!Reflect.getMetadata(GLOBAL_MODULE_METADATA, metatype);
  }
  public addGlobalModule(module: Module) {
    this.globalModules.add(module);
  }
  public getModules(): ModulesContainer {
    return this.modules;
  }
  public getModuleByKey(moduleKey: string): Module {
    return this.modules.get(moduleKey);
  }
  public getInternalCoreModuleRef(): Module | undefined {
    return this.internalCoreModule;
  }
  public async addImport(
    relatedModule: Type<any> | DynamicModule,
    token: string,
  ) {
    if (!this.modules.has(token)) {
      return;
    }
    const moduleRef = this.modules.get(token);
    const { token: relatedModuleToken } = await this.moduleCompiler.compile(
      relatedModule,
    );
    const related = this.modules.get(relatedModuleToken);
    moduleRef.addRelatedModule(related);
  }
  public addProvider(provider: Provider, token: string): string {
    if (!provider) {
      throw new CircularDependencyException();
    }
    if (!this.modules.has(token)) {
      throw new UnknownModuleException();
    }
    const moduleRef = this.modules.get(token);
    return moduleRef.addProvider(provider);
  }
  public addInjectable(
    injectable: Provider,
    token: string,
    host?: Type<Injectable>,
  ) {
    if (!this.modules.has(token)) {
      throw new UnknownModuleException();
    }
    const moduleRef = this.modules.get(token);
    moduleRef.addInjectable(injectable, host);
  }
  public addExportedProvider(provider: Type<any>, token: string) {
    if (!this.modules.has(token)) {
      throw new UnknownModuleException();
    }
    const moduleRef = this.modules.get(token);
    moduleRef.addExportedProvider(provider);
  }
  public addController(controller: Type<any>, token: string) {
    if (!this.modules.has(token)) {
      throw new UnknownModuleException();
    }
    const moduleRef = this.modules.get(token);
    moduleRef.addController(controller);
  }
  public clear() {
    this.modules.clear();
  }
  public replace(toReplace: any, options: any & { scope: any[] | null }) {
    this.modules.forEach(moduleRef => moduleRef.replace(toReplace, options));
  }
  public bindGlobalScope() {
    this.modules.forEach(moduleRef => this.bindGlobalsToImports(moduleRef));
  }
  public bindGlobalsToImports(moduleRef: Module) {
    this.globalModules.forEach(globalModule =>
      this.bindGlobalModuleToModule(moduleRef, globalModule),
    );
  }
  public bindGlobalModuleToModule(target: Module, globalModule: Module) {
    if (target === globalModule || target === this.internalCoreModule) {
      return;
    }
    target.addRelatedModule(globalModule);
  }
  public getDynamicMetadataByToken(
    token: string,
    metadataKey: keyof DynamicModule,
  ) {
    const metadata = this.dynamicModulesMetadata.get(token);
    if (metadata && metadata[metadataKey]) {
      return metadata[metadataKey] as any[];
    }
    return [];
  }
  public createCoreModule(): DynamicModule {
    return InternalCoreModule.register([
      {
        provide: ExternalContextCreator,
        useValue: ExternalContextCreator.fromContainer(this),
      },
      {
        provide: ModulesContainer,
        useValue: this.modules,
      },
      {
        provide: HttpAdapterHost,
        useValue: this.internalProvidersStorage.httpAdapterHost,
      },
    ]);
  }
  public registerCoreModuleRef(moduleRef: Module) {
    this.internalCoreModule = moduleRef;
    this.modules[InternalCoreModule.name] = moduleRef;
  }
  public getModuleTokenFactory(): ModuleTokenFactory {
    return this.moduleTokenFactory;
  }
  public registerRequestProvider<T = any>(request: T, contextId: ContextId) {
    const wrapper = this.internalCoreModule.getProviderByKey(REQUEST);
    wrapper.setInstanceByContextId(contextId, {
      instance: request,
      isResolved: true,
    });
  }
}


这个文件不仅仅是依赖注入,他还有其他的功能。这个文件并没有具体落实资源注入逻辑,具体的逻辑在 packages/core/injector/module.ts 文件中实现。可以看到 packages/core/injector/container.ts 文件中声明的 NestContainer 类中有一个私有属性,为 modules ,其类型为: Map<string, Module>


这样做的原因是因为 NestJS 的依赖注入是有作用域的,不同 module 中的资源如无特殊声明,是不允许被其他模块调用的。所以它要维护一个 Map ,每个 module 有自己的一套 IoC容器


接下来看一下 packages/core/injector/module.ts 中的代码:


展开查看源码

import {
  Abstract,
  ClassProvider,
  Controller,
  DynamicModule,
  ExistingProvider,
  FactoryProvider,
  Injectable,
  NestModule,
  Provider,
  ValueProvider,
} from '@nestjs/common/interfaces';
import { Type } from '@nestjs/common/interfaces/type.interface';
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
import {
  isFunction,
  isNil,
  isString,
  isSymbol,
  isUndefined,
} from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { ApplicationConfig } from '../application-config';
import { InvalidClassException } from '../errors/exceptions/invalid-class.exception';
import { RuntimeException } from '../errors/exceptions/runtime.exception';
import { UnknownExportException } from '../errors/exceptions/unknown-export.exception';
import { createContextId } from '../helpers';
import { getClassScope } from '../helpers/get-class-scope';
import { CONTROLLER_ID_KEY } from './constants';
import { NestContainer } from './container';
import { InstanceWrapper } from './instance-wrapper';
import { ModuleRef } from './module-ref';
interface ProviderName {
  name?: string | symbol;
}
export class Module {
  private readonly _id: string;
  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<
    string,
    InstanceWrapper<Controller>
  >();
  private readonly _exports = new Set<string | symbol>();
  private _distance = 0;
  constructor(
    private readonly _metatype: Type<any>,
    private readonly container: NestContainer,
  ) {
    this.addCoreProviders();
    this._id = randomStringGenerator();
  }
  get id(): string {
    return this._id;
  }
  get providers(): Map<any, InstanceWrapper<Injectable>> {
    return this._providers;
  }
  get middlewares(): Map<any, InstanceWrapper<Injectable>> {
    return this._middlewares;
  }
  get imports(): Set<Module> {
    return this._imports;
  }
  /**
   * Left for backward-compatibility reasons
   */
  get relatedModules(): Set<Module> {
    return this._imports;
  }
  /**
   * Left for backward-compatibility reasons
   */
  get components(): Map<string, InstanceWrapper<Injectable>> {
    return this._providers;
  }
  /**
   * Left for backward-compatibility reasons
   */
  get routes(): Map<string, InstanceWrapper<Controller>> {
    return this._controllers;
  }
  get injectables(): Map<string, InstanceWrapper<Injectable>> {
    return this._injectables;
  }
  get controllers(): Map<string, InstanceWrapper<Controller>> {
    return this._controllers;
  }
  get exports(): Set<string | symbol> {
    return this._exports;
  }
  get instance(): NestModule {
    if (!this._providers.has(this._metatype.name)) {
      throw new RuntimeException();
    }
    const module = this._providers.get(this._metatype.name);
    return module.instance as NestModule;
  }
  get metatype(): Type<any> {
    return this._metatype;
  }
  get distance(): number {
    return this._distance;
  }
  set distance(value: number) {
    this._distance = value;
  }
  public addCoreProviders() {
    this.addModuleAsProvider();
    this.addModuleRef();
    this.addApplicationConfig();
  }
  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,
      }),
    );
  }
  public addInjectable<T extends Injectable>(
    injectable: Provider,
    host?: Type<T>,
  ) {
    if (this.isCustomProvider(injectable)) {
      return this.addCustomProvider(injectable, this._injectables);
    }
    let instanceWrapper = this.injectables.get(injectable.name);
    if (!instanceWrapper) {
      instanceWrapper = new InstanceWrapper({
        name: injectable.name,
        metatype: injectable,
        instance: null,
        isResolved: false,
        scope: getClassScope(injectable),
        host: this,
      });
      this._injectables.set(injectable.name, instanceWrapper);
    }
    if (host) {
      const token = host && host.name;
      const hostWrapper =
        this._controllers.get(host && host.name) || this._providers.get(token);
      hostWrapper && hostWrapper.addEnhancerMetadata(instanceWrapper);
    }
  }
  public addProvider(provider: Provider): string {
    if (this.isCustomProvider(provider)) {
      return this.addCustomProvider(provider, this._providers);
    }
    this._providers.set(
      (provider as Type<Injectable>).name,
      new InstanceWrapper({
        name: (provider as Type<Injectable>).name,
        metatype: provider as Type<Injectable>,
        instance: null,
        isResolved: false,
        scope: getClassScope(provider),
        host: this,
      }),
    );
    return (provider as Type<Injectable>).name;
  }
  public isCustomProvider(
    provider: Provider,
  ): provider is
    | ClassProvider
    | FactoryProvider
    | ValueProvider
    | ExistingProvider {
    return !isNil(
      (provider as
        | ClassProvider
        | FactoryProvider
        | ValueProvider
        | ExistingProvider).provide,
    );
  }
  public addCustomProvider(
    provider: (
      | ClassProvider
      | FactoryProvider
      | ValueProvider
      | ExistingProvider
    ) &
      ProviderName,
    collection: Map<string, any>,
  ): string {
    const name = this.getProviderStaticToken(provider.provide) as string;
    provider = {
      ...provider,
      name,
    };
    if (this.isCustomClass(provider)) {
      this.addCustomClass(provider, collection);
    } else if (this.isCustomValue(provider)) {
      this.addCustomValue(provider, collection);
    } else if (this.isCustomFactory(provider)) {
      this.addCustomFactory(provider, collection);
    } else if (this.isCustomUseExisting(provider)) {
      this.addCustomUseExisting(provider, collection);
    }
    return name;
  }
  public isCustomClass(provider: any): provider is ClassProvider {
    return !isUndefined((provider as ClassProvider).useClass);
  }
  public isCustomValue(provider: any): provider is ValueProvider {
    return !isUndefined((provider as ValueProvider).useValue);
  }
  public isCustomFactory(provider: any): provider is FactoryProvider {
    return !isUndefined((provider as FactoryProvider).useFactory);
  }
  public isCustomUseExisting(provider: any): provider is ExistingProvider {
    return !isUndefined((provider as ExistingProvider).useExisting);
  }
  public isDynamicModule(exported: any): exported is DynamicModule {
    return exported && exported.module;
  }
  public addCustomClass(
    provider: ClassProvider & ProviderName,
    collection: Map<string, InstanceWrapper>,
  ) {
    const { name, useClass } = provider;
    let { scope } = provider;
    if (isUndefined(scope)) {
      scope = getClassScope(useClass);
    }
    collection.set(
      name as string,
      new InstanceWrapper({
        name,
        metatype: useClass,
        instance: null,
        isResolved: false,
        scope,
        host: this,
      }),
    );
  }
  public addCustomValue(
    provider: ValueProvider & ProviderName,
    collection: Map<string, InstanceWrapper>,
  ) {
    const { name, useValue: value } = provider;
    collection.set(
      name as string,
      new InstanceWrapper({
        name,
        metatype: null,
        instance: value,
        isResolved: true,
        async: value instanceof Promise,
        host: this,
      }),
    );
  }
  public addCustomFactory(
    provider: FactoryProvider & ProviderName,
    collection: Map<string, InstanceWrapper>,
  ) {
    const { name, useFactory: factory, inject, scope } = provider;
    collection.set(
      name as string,
      new InstanceWrapper({
        name,
        metatype: factory as any,
        instance: null,
        isResolved: false,
        inject: inject || [],
        scope,
        host: this,
      }),
    );
  }
  public addCustomUseExisting(
    provider: ExistingProvider & ProviderName,
    collection: Map<string, InstanceWrapper>,
  ) {
    const { name, useExisting } = provider;
    collection.set(
      name as string,
      new InstanceWrapper({
        name,
        metatype: (instance => instance) as any,
        instance: null,
        isResolved: false,
        inject: [useExisting],
        host: this,
        isAlias: true,
      }),
    );
  }
  public addExportedProvider(
    provider: (Provider & ProviderName) | string | symbol | DynamicModule,
  ) {
    const addExportedUnit = (token: string | symbol) =>
      this._exports.add(this.validateExportedProvider(token));
    if (this.isCustomProvider(provider as any)) {
      return this.addCustomExportedProvider(provider as any);
    } else if (isString(provider) || isSymbol(provider)) {
      return addExportedUnit(provider);
    } else if (this.isDynamicModule(provider)) {
      const { module } = provider;
      return addExportedUnit(module.name);
    }
    addExportedUnit(provider.name);
  }
  public addCustomExportedProvider(
    provider:
      | FactoryProvider
      | ValueProvider
      | ClassProvider
      | ExistingProvider,
  ) {
    const provide = provider.provide;
    if (isString(provide) || isSymbol(provide)) {
      return this._exports.add(this.validateExportedProvider(provide));
    }
    this._exports.add(this.validateExportedProvider(provide.name));
  }
  public validateExportedProvider(token: string | symbol) {
    if (this._providers.has(token)) {
      return token;
    }
    const importsArray = [...this._imports.values()];
    const importsNames = iterate(importsArray)
      .filter(item => !!item)
      .map(({ metatype }) => metatype)
      .filter(metatype => !!metatype)
      .map(({ name }) => name)
      .toArray();
    if (!importsNames.includes(token as string)) {
      const { name } = this.metatype;
      throw new UnknownExportException(token, name);
    }
    return token;
  }
  public addController(controller: Type<Controller>) {
    this._controllers.set(
      controller.name,
      new InstanceWrapper({
        name: controller.name,
        metatype: controller,
        instance: null,
        isResolved: false,
        scope: getClassScope(controller),
        host: this,
      }),
    );
    this.assignControllerUniqueId(controller);
  }
  public assignControllerUniqueId(controller: Type<Controller>) {
    Object.defineProperty(controller, CONTROLLER_ID_KEY, {
      enumerable: false,
      writable: false,
      configurable: true,
      value: randomStringGenerator(),
    });
  }
  public addRelatedModule(module: Module) {
    this._imports.add(module);
  }
  public replace(toReplace: string | symbol | Type<any>, options: any) {
    if (options.isProvider && this.hasProvider(toReplace)) {
      const name = this.getProviderStaticToken(toReplace);
      const originalProvider = this._providers.get(name);
      return originalProvider.mergeWith({ provide: toReplace, ...options });
    } else if (!options.isProvider && this.hasInjectable(toReplace)) {
      const name = this.getProviderStaticToken(toReplace);
      const originalInjectable = this._injectables.get(name);
      return originalInjectable.mergeWith({
        provide: toReplace,
        ...options,
      });
    }
  }
  public hasProvider(token: string | symbol | Type<any>): boolean {
    const name = this.getProviderStaticToken(token);
    return this._providers.has(name);
  }
  public hasInjectable(token: string | symbol | Type<any>): boolean {
    const name = this.getProviderStaticToken(token);
    return this._injectables.has(name);
  }
  public getProviderStaticToken(
    provider: string | symbol | Type<any> | Abstract<any>,
  ): string | symbol {
    return isFunction(provider)
      ? (provider as Function).name
      : (provider as string | symbol);
  }
  public getProviderByKey<T = any>(name: string | symbol): InstanceWrapper<T> {
    return this._providers.get(name) as InstanceWrapper<T>;
  }
  public getNonAliasProviders(): Array<[string, InstanceWrapper<Injectable>]> {
    return [...this._providers].filter(([_, wrapper]) => !wrapper.isAlias);
  }
  public createModuleReferenceType(): Type<ModuleRef> {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const self = this;
    return class extends ModuleRef {
      constructor() {
        super(self.container);
      }
      public get<TInput = any, TResult = TInput>(
        typeOrToken: Type<TInput> | string | symbol,
        options: { strict: boolean } = { strict: true },
      ): TResult {
        return !(options && options.strict)
          ? this.find<TInput, TResult>(typeOrToken)
          : this.find<TInput, TResult>(typeOrToken, self);
      }
      public resolve<TInput = any, TResult = TInput>(
        typeOrToken: Type<TInput> | string | symbol,
        contextId = createContextId(),
        options: { strict: boolean } = { strict: true },
      ): Promise<TResult> {
        return this.resolvePerContext(typeOrToken, self, contextId, options);
      }
      public async create<T = any>(type: Type<T>): Promise<T> {
        if (!(type && isFunction(type) && type.prototype)) {
          throw new InvalidClassException(type);
        }
        return this.instantiateClass<T>(type, self);
      }
    };
  }
}


这面的代码更多,我们先专注于 _providers 这个私有属性,可以发现它的类型是: Map<any, InstanceWrapper<Injectable>> ,这就是Demo中的私有属性 providers !当然, nestjs 中的功能更加的完善,类型也更加的复杂。


下面看一下核心方法 addProvider()

public addProvider(provider: Provider): string {
  if (this.isCustomProvider(provider)) {
    return this.addCustomProvider(provider, this._providers);
  }
  this._providers.set(
    (provider as Type<Injectable>).name,
    new InstanceWrapper({
      name: (provider as Type<Injectable>).name,
      metatype: provider as Type<Injectable>,
      instance: null,
      isResolved: false,
      scope: getClassScope(provider),
      host: this,
    }),
  );
  return (provider as Type<Injectable>).name;
}点击复制复制失败已复制


首先,它判断了这个 Provider 是否是自定义的,通过阅读源码,发现判断依据为 provider 中的 provide 属性是否为 nullundefined 。这个写过自定义 provider 的都明白。先看一下自定义的 Provider 处理逻辑:

public addCustomProvider(
  provider: (
    | ClassProvider
    | FactoryProvider
    | ValueProvider
    | ExistingProvider
  ) &
    ProviderName,
  collection: Map<string, any>,
): string {
  const name = this.getProviderStaticToken(provider.provide) as string;
  provider = {
    ...provider,
    name,
  };
  if (this.isCustomClass(provider)) {
    this.addCustomClass(provider, collection);
  } else if (this.isCustomValue(provider)) {
    this.addCustomValue(provider, collection);
  } else if (this.isCustomFactory(provider)) {
    this.addCustomFactory(provider, collection);
  } else if (this.isCustomUseExisting(provider)) {
    this.addCustomUseExisting(provider, collection);
  }
  return name;
}点击复制复制失败已复制


在这里,它先获取了 name ,这个 name 就是Demo中的 token ,作为标识使用。下面的代码就是按照不同的 Provider类型 将其插入到 _providers 私有属性中,具体插入方法很简单,篇幅原因,不再赘述。

接下来分析一下注入方法

目录
相关文章
|
5月前
|
Java 测试技术 数据库连接
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
|
5月前
|
缓存 安全 Java
谈谈springboot的装饰者模式
【4月更文挑战第14天】在 Spring Boot 中,装饰者模式被广泛应用于增强和扩展现有功能,同时保持核心逻辑的清晰和不变。这种模式在处理请求、响应、以及各种中间件中尤为常见,通过包装一个或多个组件来增加额外的行为或修改现有行为
144 2
|
5月前
|
XML Java 开发者
【Spring源码解读 底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理讲解
【Spring源码解读 底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理讲解
|
5月前
|
存储 前端开发 安全
Spring框架: 解释一下Spring框架的核心概念。
Spring框架: 解释一下Spring框架的核心概念。
60 1
|
XML JSON Java
SpingBoot原理
1. 配置优先级:Springboot项目当中属性配置的常见方式以及配置的优先级 2. Bean的管理 3. 剖析Springboot的底层原理
62 0
|
Java Spring 容器
【框架源码】Spring源码核心注解@Conditional原理及应用
【框架源码】Spring源码核心注解@Conditional原理及应用
【框架源码】Spring源码核心注解@Conditional原理及应用
|
移动开发 前端开发 JavaScript
DSBridge框架使用说明
DSBridge框架使用说明
724 0
|
缓存 Oracle JavaScript
浅谈NestJS设计思想(分层、IOC、AOP)
浅谈NestJS设计思想(分层、IOC、AOP) nestJS用了有一定时间了,当初学习node后端选择的第一个web框架,这篇文章将对NestJS框架层面的几个重要概念进行梳理,希望能加深记忆,融汇贯通,更进一步,本文阅读需要对nestJS有一定使用经验。
400 0
|
JSON 前端开发 数据可视化
umi3源码探究简析
作为蚂蚁金服整个生态圈最为核心的部分,umi可谓是王冠上的红宝石,因而个人认为对于整个umi架构内核的学习及设计哲学的理解,可能比如何使用要来的更为重要;作为一个使用者,希望能从各位大佬的源码中汲取一些养分以及获得一些灵感
225 0
|
Java 应用服务中间件 程序员
Spring源码之六-onRefresh()方法
今天带大家解读Spirng源码之六的onRefresh()方法,这是refresh()的其中的一个方法,看似是一个空方法,实则他是非常非常重要的,对于提高Spring的扩展性。