美文网首页
Angular Injector 分析

Angular Injector 分析

作者: niccky | 来源:发表于2020-09-06 09:39 被阅读0次

Injector 类

静态类型的有:

  • THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
  • NULL = new NullInjector()
  • create(options, parent, name)

抽象类型的有:

  • get(token, notFound, flags)
export abstract class Injector {
  static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
  static NULL: Injector = new NullInjector();
  abstract get<T>(
      token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T, flags?: InjectFlags): T;
  static create(providers: StaticProvider[], parent?: Injector): Injector;
  static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector;
  static create(
      options: StaticProvider[]|{providers: StaticProvider[], parent?: Injector, name?: string},
      parent?: Injector): Injector {
    if (Array.isArray(options)) {
      return new StaticInjector(options, parent, '');
    } else {
        return new StaticInjector(options.providers, options.parent, options.name || '');
    }
  }

  static __NG_ELEMENT_ID__ = -1;
}

const 类型的辅助变量


const IDENT = function<T>(value: T): T {
  return value;
};
const EMPTY = <any[]>[];
const CIRCULAR = IDENT;
const MULTI_PROVIDER_FN = function(): any[] {
  return Array.prototype.slice.call(arguments);
};

const enum OptionFlags {
  Optional = 1 << 0,
  CheckSelf = 1 << 1,
  CheckParent = 1 << 2,
  Default = CheckSelf | CheckParent
}
const NO_NEW_LINE = 'ɵ';

StaticInjector 类

在构造函数中做了几件重要事情:

  1. 接收 parent 注入器
  2. 定义一个空的 recordsMap<any, Record | null>
  3. 初始化层级 Injector (不一定准确,可以先这么理解) records.set(Injector, {token: Injector, fn: IDENT, deps: EMPTY, useValue: this, useNew: false})
  • 递归处理 providers
export class StaticInjector implements Injector {
  readonly parent: Injector;
  readonly source: string|null;
  readonly scope: string|null;

  private _records: Map<any, Record|null>;

  constructor(
      providers: StaticProvider[], parent: Injector = Injector.NULL, source: string|null = null) {
    this.parent = parent;
    this.source = source;
    const records = this._records = new Map<any, Record>();
    records.set(
        Injector, <Record>{token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
    records.set(
        INJECTOR, <Record>{token: INJECTOR, fn: IDENT, deps: EMPTY, value: this, useNew: false});
    this.scope = recursivelyProcessProviders(records, providers);
  }

  get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
  get(token: any, notFoundValue?: any): any;
  get(token: any, notFoundValue?: any, flags: InjectFlags = InjectFlags.Default): any {
    const records = this._records;
    let record = records.get(token);
    if (record === undefined) {
      // This means we have never seen this record, see if it is tree shakable provider.
      const injectableDef = getInjectableDef(token);
      if (injectableDef) {
        const providedIn = injectableDef && injectableDef.providedIn;
        if (providedIn === 'any' || providedIn != null && providedIn === this.scope) {
          records.set(
              token,
              record = resolveProvider(
                  {provide: token, useFactory: injectableDef.factory, deps: EMPTY}));
        }
      }
      if (record === undefined) {
        // Set record to null to make sure that we don't go through expensive lookup above again.
        records.set(token, null);
      }
    }
    let lastInjector = setCurrentInjector(this);
    try {
      return tryResolveToken(token, record, records, this.parent, notFoundValue, flags);
    } catch (e) {
      return catchInjectorError(e, token, 'StaticInjectorError', this.source);
    } finally {
      setCurrentInjector(lastInjector);
    }
  }

  toString() {
    const tokens = <string[]>[], records = this._records;
    records.forEach((v, token) => tokens.push(stringify(token)));
    return `StaticInjector[${tokens.join(', ')}]`;
  }
}

type SupportedProvider =
    ValueProvider|ExistingProvider|StaticClassProvider|ConstructorProvider|FactoryProvider;

interface Record {
  fn: Function;
  useNew: boolean;
  deps: DependencyRecord[];
  value: any;
}

interface DependencyRecord {
  token: any;
  options: number;
}


function multiProviderMixError(token: any) {
  return staticError('Cannot mix multi providers and regular providers', token);
}

recursivelyProcessProviders

provider 分两类处理:

  • provider 是个 providers 数组
  • provider 是个普通标准的 provider 对象

如果 provider 是个数组,则递归处理 provider; 如果是普通 povider 则按照正常流程走.

正常流程有这么几类 provider

  • 单个覆盖配置 multi: false
  • 多个不覆盖配置 multi: true

如果 multi: true 就用 provider.provide 配置一条空的 record, record 内容如下:

{
    token: provider.provide,
    deps: [],
    useNew: false,
    fn: MULTI_PROVIDER_FN,
    value: EMPTY
}

然后把 {token, options: OptionFlags.Default} 作为上面这条空记录的依赖 (deps), 其中 token 为一个大对象 provider.

multi: true 走完了,就按正常流程走,开始用 provider.provider 作为 token,配置 record 记录, records.set(token, resolvedProvider).

function recursivelyProcessProviders(records: Map<any, Record>, provider: StaticProvider): string|
    null {
  let scope: string|null = null;
  if (provider) {
    provider = resolveForwardRef(provider);
    if (Array.isArray(provider)) {
      // if we have an array recurse into the array
      for (let i = 0; i < provider.length; i++) {
        scope = recursivelyProcessProviders(records, provider[i]) || scope;
      }
    } else if (typeof provider === 'function') {
      // Functions were supported in ReflectiveInjector, but are not here. For safety give useful
      // error messages
      throw staticError('Function/Class not supported', provider);
    } else if (provider && typeof provider === 'object' && provider.provide) {
      // At this point we have what looks like a provider: {provide: ?, ....}
      let token = resolveForwardRef(provider.provide);
      const resolvedProvider = resolveProvider(provider);
      if (provider.multi === true) {
        // This is a multi provider.
        let multiProvider: Record|undefined = records.get(token);
        if (multiProvider) {
          if (multiProvider.fn !== MULTI_PROVIDER_FN) {
            throw multiProviderMixError(token);
          }
        } else {
          // Create a placeholder factory which will look up the constituents of the multi provider.
          records.set(token, multiProvider = <Record>{
            token: provider.provide,
            deps: [],
            useNew: false,
            fn: MULTI_PROVIDER_FN,
            value: EMPTY
          });
        }
        // Treat the provider as the token.
        token = provider;
        multiProvider.deps.push({token, options: OptionFlags.Default});
      }
      const record = records.get(token);
      if (record && record.fn == MULTI_PROVIDER_FN) {
        throw multiProviderMixError(token);
      }
      if (token === INJECTOR_SCOPE) {
        scope = resolvedProvider.value;
      }
      records.set(token, resolvedProvider);
    } else {
      throw staticError('Unexpected provider', provider);
    }
  }
  return scope;
}

resolveProvider

先定义几个 provider 示例:

  • useClass { provide: LocationStrategy, useClass: HashLocationStrategy, deps: [Injector,[new Optional() , ClientBrowser]]}
  • useExisting { provide: LocationStrategy, useExisting: HashLocationStrategy,deps: [Injector,] }
  • useValue { provide: LocationStrategy, useValue: 'HashLocationStrategy', deps:[Injector,] }
  • useFactory { provide: LocationStrategy, useFactory: () => HashLocationStrategy, deps: [Injector,] }
  • Function``LocationStrategy (一个普通的构造函数)

此函数主要干这么几件事情:

  • 解析 providerdeps [computeDeps]
  • 找出 provider 的具体类型,如: useClass, useValue, useFactory ...
  • value 记录 useValue
  • fn 记录 useFactory , useClass, function
  • useExisting 在这什么也不干
  • 返回解析出来带得有依赖的一条记录 record, {deps, fn, useNew, value}

其中 useClass, functionuseNewtrue. 如果 useNew: true, 则应用程序在取东西的时候需要通过类似这样的处理, return useNew ? new fn(...deps) : fn.apply(obj = undefined, deps);

function resolveProvider(provider: SupportedProvider): Record {
  const deps = computeDeps(provider);
  let fn: Function = IDENT;
  let value: any = EMPTY;
  let useNew: boolean = false;
  let provide = resolveForwardRef(provider.provide);
  if (USE_VALUE in provider) {
    // We need to use USE_VALUE in provider since provider.useValue could be defined as undefined.
    value = (provider as ValueProvider).useValue;
  } else if ((provider as FactoryProvider).useFactory) {
    fn = (provider as FactoryProvider).useFactory;
  } else if ((provider as ExistingProvider).useExisting) {
    // Just use IDENT
  } else if ((provider as StaticClassProvider).useClass) {
    useNew = true;
    fn = resolveForwardRef((provider as StaticClassProvider).useClass);
  } else if (typeof provide == 'function') {
    useNew = true;
    fn = provide;
  } else {
    throw staticError(
        'StaticProvider does not have [useValue|useFactory|useExisting|useClass] or [provide] is not newable',
        provider);
  }
  return {deps, fn, useNew, value};
}

computeDeps

示例 {provide: ?, deps: [Optional, Skip, SkipSelf]}

按照 provider.deps 配置的依赖,使用遍历的方式一个一个的找出来,然后判断是什么类型的依赖,如: Optional, Self, SkipSelf, Inject ..., 最后把找出来的依赖组合成新对象,如: [{token, option}].

到这里 StaticInjector#create 算是走完了

function computeDeps(provider: StaticProvider): DependencyRecord[] {
  let deps: DependencyRecord[] = EMPTY;
  const providerDeps: any[] =
      (provider as ExistingProvider & StaticClassProvider & ConstructorProvider).deps;
  if (providerDeps && providerDeps.length) {
    deps = [];
    for (let i = 0; i < providerDeps.length; i++) {
      let options = OptionFlags.Default;
      let token = resolveForwardRef(providerDeps[i]);
      if (Array.isArray(token)) {
        for (let j = 0, annotations = token; j < annotations.length; j++) {
          const annotation = annotations[j];
          if (annotation instanceof Optional || annotation == Optional) {
            options = options | OptionFlags.Optional;
          } else if (annotation instanceof SkipSelf || annotation == SkipSelf) {
            options = options & ~OptionFlags.CheckSelf;
          } else if (annotation instanceof Self || annotation == Self) {
            options = options & ~OptionFlags.CheckParent;
          } else if (annotation instanceof Inject) {
            token = (annotation as Inject).token;
          } else {
            token = resolveForwardRef(annotation);
          }
        }
      }
      deps.push({token, options});
    }
  } else if ((provider as ExistingProvider).useExisting) {
    const token = resolveForwardRef((provider as ExistingProvider).useExisting);
    deps = [{token, options: OptionFlags.Default}];
  } else if (!providerDeps && !(USE_VALUE in provider)) {
    // useValue & useExisting are the only ones which are exempt from deps all others need it.
    throw staticError('\'deps\' required', provider);
  }
  return deps;
}

tryResolveToken

StaticInject#get 参数列表为(token: any, notFoundValue?: any, flags: InjectFlags = InjectFlags.Default), 其中 notFoundValue = undefined, flags: InjectFlags = InjectFlags.Default,这个默认值后面会用到.

function tryResolveToken(
    token: any, record: Record|undefined|null, records: Map<any, Record|null>, parent: Injector,
    notFoundValue: any, flags: InjectFlags): any {
  try {
    return resolveToken(token, record, records, parent, notFoundValue, flags);
  } catch (e) {
    // ensure that 'e' is of type Error.
    if (!(e instanceof Error)) {
      e = new Error(e);
    }
    const path: any[] = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
    path.unshift(token);
    if (record && record.value == CIRCULAR) {
      // Reset the Circular flag.
      record.value = EMPTY;
    }
    throw e;
  }
}

resolveToken

resolveToken 这开始使用 resolveProvider 解析组合出来的 record 创建所需的对象出来。

flags 默认为 InjectFlags.Default, 关于 InjectFlags 相关的东西会在后面一一揭晓.

在这个函数中主要干这么几件事情:

  1. flags 辅助判断各种配型的配置,如: SkipSelf, Self, Optional, Default
  2. 根据 flags 计算出来的结果,然后决定以什么方式去取这个东西出来,如: new fn(...),fn.apply(undefined, deps), Inject.NULL.get(token,...)

如果 provider.deps 有配置依赖 deps,那就递归一个个取出来放到 deps[...] 里面,当 provider.deps 全部取完后,就开始创建对象了。

创建对象的两种方式:

  • useNew: true new fn(...deps)
  • useNew: false fn.apply(obj = undefined, deps)
function resolveToken(
    token: any, record: Record|undefined|null, records: Map<any, Record|null>, parent: Injector,
    notFoundValue: any, flags: InjectFlags): any {
  let value;
  if (record && !(flags & InjectFlags.SkipSelf)) {
    // If we don't have a record, this implies that we don't own the provider hence don't know how
    // to resolve it.
    value = record.value;
    if (value == CIRCULAR) {
      throw Error(NO_NEW_LINE + 'Circular dependency');
    } else if (value === EMPTY) {
      record.value = CIRCULAR;
      let obj = undefined;
      let useNew = record.useNew;
      let fn = record.fn;
      let depRecords = record.deps;
      let deps = EMPTY;
      if (depRecords.length) {
        deps = [];
        for (let i = 0; i < depRecords.length; i++) {
          const depRecord: DependencyRecord = depRecords[i];
          const options = depRecord.options;
          const childRecord =
              options & OptionFlags.CheckSelf ? records.get(depRecord.token) : undefined;
          deps.push(tryResolveToken(
              // Current Token to resolve
              depRecord.token,
              // A record which describes how to resolve the token.
              // If undefined, this means we don't have such a record
              childRecord,
              // Other records we know about.
              records,
              // If we don't know how to resolve dependency and we should not check parent for it,
              // than pass in Null injector.
              !childRecord && !(options & OptionFlags.CheckParent) ? Injector.NULL : parent,
              options & OptionFlags.Optional ? null : Injector.THROW_IF_NOT_FOUND,
              InjectFlags.Default));
        }
      }
      record.value = value = useNew ? new (fn as any)(...deps) : fn.apply(obj, deps);
    }
  } else if (!(flags & InjectFlags.Self)) {
    value = parent.get(token, notFoundValue, InjectFlags.Default);
  } else if (!(flags & InjectFlags.Optional)) {
    value = Injector.NULL.get(token, notFoundValue);
  } else {
    value = Injector.NULL.get(token, typeof notFoundValue !== 'undefined' ? notFoundValue : null);
  }
  return value;
}
function staticError(text: string, obj: any): Error {
  return new Error(formatError(text, obj, 'StaticInjectorError'));
}

InjectFlags

{
    Default: 0,
    Host: 1,
    Self: 2,
    SkipSelf: 4,
    Optional: 8

}

InjectFlags enum

export enum InjectFlags {
  // TODO(alxhub): make this 'const' when ngc no longer writes exports of it into ngfactory files.

  /** Check self and check parent injector if needed */
  Default = 0b0000, // Default = 0
  /**
   * Specifies that an injector should retrieve a dependency from any injector until reaching the
   * host element of the current component. (Only used with Element Injector)
   */
  Host = 0b0001,    // Host = 1
  /** Don't ascend to ancestors of the node requesting injection. */
  Self = 0b0010,    // Self = 2
  /** Skip the node that is requesting injection. */
  SkipSelf = 0b0100,    // SkipSelf = 4
  /** Inject `defaultValue` instead if token not found. */
  Optional = 0b1000,    // Optional = 8
}

OptionFlags

const enum OptionFlags {
  Optional = 1 << 0,        // 0b0001 = 1
  CheckSelf = 1 << 1,       // 0b0010 = 2
  CheckParent = 1 << 2,     // 0b0100 = 4
  Default = CheckSelf | CheckParent // 0b0110 = 6
}

相关文章

  • Angular Injector 分析

    Injector 类 静态类型的有: THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUN...

  • provider实现原理

    原生angular中的provider和$injector主要作用是: 注册组件(controller direc...

  • SAP 电商云 Spartacus UI 的 proxy fac

    下列代码第 126 行,通过来自 Angular/core 的 Injector,手动注入一个 QuickOrde...

  • Angular 2 之七 依赖注入

    概述Angular 2使用自己的依赖注入框架并采用构造注入方式,依赖注入分两个步骤: 1) 向injector注册...

  • angular 依赖注入(DI)

    1.依赖注入(DI) DI是一个设计模式,处理组件如何获取依赖。angular中 injector子系统专门负责创...

  • Injector的实现原理

    在分析bootstrapModule方法时,我们发现当需要一个类的事例的时候只需要调用injector.get方法...

  • Angular.js

    强推! angular 模块依赖分析 可视化哦 Create graphs of your angular pro...

  • @Host

    Specifies that an injector should retrieve a dependency f...

  • Motan Restful Server分析

    入口点在RestfulServletContainerListener类,通过resteasy.injector....

  • err 错误

    1.Error: [$injector:unpr] Unknown provider: tendateFilter...

网友评论

      本文标题:Angular Injector 分析

      本文链接:https://www.haomeiwen.com/subject/riaqektx.html