angular源码剖析之StaticInjector

上一篇说到了平台实例在初始化的时刻会建立根注入器,那如今就一同看看注入器是怎样建立的,又是怎样事情的.(一切援用的代码都被简化了)

建立注入器

顺序初始化时挪用的建立根注入器的静态要领:

abstract class 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 || null);
  }
}

挪用此要领会返回一个StaticInjector范例的实例(也就是注入器).

StaticInjector

export class StaticInjector implements Injector {
  readonly parent: Injector;
  readonly source: string|null;
  private _records: Map<any, Record>;
  constructor(providers: StaticProvider[], parent: Injector = NULL_INJECTOR, 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});
    recursivelyProcessProviders(records, providers);
  }
}

注入器的组织函数在初始化历程当中的操纵:

  • 设置当前注入器的父注入器
  • 设置注入器的源
  • 新建注册表(_records属性,是一个Map范例)
  • 将参数providers悉数增加到注册表中

向注册表中增加效劳挪用了recursivelyProcessProviders函数

const EMPTY = <any[]>[];
const MULTI_PROVIDER_FN = function (): any[] { return Array.prototype.slice.call(arguments) };
function recursivelyProcessProviders(records: Map<any, Record>, provider: StaticProvider) {
    if (provider instanceof Array) {
      for (let i = 0; i < provider.length; i++) {
        recursivelyProcessProviders(records, provider[i]);
      }
    } else (provider && typeof provider === 'object' && provider.provide) {
      let token = resolveForwardRef(provider.provide);// 要领`resolveForwardRef`的作用多是向前兼容,能够疏忽
      const resolvedProvider = resolveProvider(provider);
      if (provider.multi === true) {
        let multiProvider: Record | undefined = records.get(token);
        if (multiProvider) {
          if (multiProvider.fn !== MULTI_PROVIDER_FN) {
            throw multiProviderMixError(token);
          }
        } else {
          records.set(token, multiProvider = <Record>{
            token: provider.provide,
            deps: [],
            useNew: false, // 这个值在后面猎取依靠实例的时刻会用到,当作推断前提
            fn: MULTI_PROVIDER_FN,
            value: EMPTY // 这个值在后面猎取依靠实例的时刻会用到,当作推断前提
          });
        }
        token = provider;
        multiProvider.deps.push({ token, options: OptionFlags.Default });
      }
      records.set(token, resolvedProvider);
    }
}

recursivelyProcessProviders函数详细的实行历程:

假如provider是个数组,那就遍历后顺次挪用此要领.

假如provider是个对象:

1 猎取token

  let token = resolveForwardRef(provider.provide);

2 挪用resolveProvider要领处置惩罚效劳中能够涌现的属性和依靠,返回一个Record对象,此对象会作为token的值<!– (useValue,useClass,deps,useExisting,useFactory) –>

function resolveProvider(provider: SupportedProvider): Record {
  const deps = computeDeps(provider);
  let fn: Function = function (value) { return value };
  let value: any = [];
  // useUew用来标识是不是须要 new 
  let useNew: boolean = false;
  let provide = resolveForwardRef(provider.provide);
  if (USE_VALUE in provider) {
    value = provider.useValue;
  } else if (provider.useFactory) {
    fn = provider.useFactory;
  } else if (provider.useExisting) {
    //do nothing
  } else if (provider.useClass) {
    useNew = true;
    fn = resolveForwardRef(provider.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 }; // provider中差别的属性会返回包括差别值的对象
}

这个要领会先挪用computeDeps函数处置惩罚效劳须要的依靠,它将useExisting范例的效劳也转换成deps,末了返回[{ token, OptionFlags }]情势的数组(OptionFlags是罗列常量)

  function computeDeps(provider: StaticProvider): DependencyRecord[] {
    let deps: DependencyRecord[] = EMPTY;
    const providerDeps: any[] = provider.deps;
    if (providerDeps && providerDeps.length) {
      deps = [];
      for (let i = 0; i < providerDeps.length; i++) {
        let options = OptionFlags.Default;
        let token = resolveForwardRef(providerDeps[i]);
        deps.push({ token, options });
      }
    } else if ((provider as ExistingProvider).useExisting) {
      const token = resolveForwardRef((provider as ExistingProvider).useExisting);
      deps = [{ token, options: OptionFlags.Default }];
    } 
    return deps;
  }

resolveProvider函数终究返回的Record对象有一个缺省值:

{
  deps:[], // 包括依靠时 [{ token, options },{ token, options }]
  fn:function(value) { return value },
  useNew:false,
  value:[]
}

实行历程当中会依据provider差别的属性修正Record对象的变量为差别的值:

  • useValue : 修正valueuseValue的值
  • useFactory : 修正fn为对应的函数
  • useClasstypeof provide == 'function'(令牌为一个函数时) : 修正fn为对应的函数,并设置useNewtrue
  • useExisting : 不做修正,直接运用默认值

3 假如是多处置惩罚效劳(multi:ture)且为初次注册,那末在注册表中分外注册一个占位的Record

records.set(token, multiProvider = <Record>{
  token: provider.provide,
  deps: [],
  useNew: false,
  fn: MULTI_PROVIDER_FN,
  value: EMPTY
});

4 非多处置惩罚效劳以token为键,多处置惩罚效劳以provider对象为键,返回的Record对象为值,存入注册表records

从注入器中猎取实例

效劳注册完,下一步就是怎样从注入器中猎取效劳的实例了,这会挪用StaticInjectorget要领

export class StaticInjector implements Injector {
  get(token: any, notFoundValue?: any, flags: InjectFlags = InjectFlags.Default): any {
    // 猎取token对应的record
    const record = this._records.get(token);
    return resolveToken(token, record, this._records, this.parent, notFoundValue, flags);
}

get要领挪用了resolveToken函数,这个函数会返回token对应的实例(就是被注入的对象)

const EMPTY = <any[]>[];
const CIRCULAR = IDENT;
const IDENT = function <T>(value: T): T { return value };
function resolveToken(token: any, record: Record | undefined, records: Map<any, Record>, parent: Injector,
  notFoundValue: any, flags: InjectFlags): any {
  let value;
  if (record && !(flags & InjectFlags.SkipSelf)) {
    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(
            depRecord.token,
            childRecord,
            records,
            !childRecord && !(options & OptionFlags.CheckParent) ? NULL_INJECTOR : 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);
  }
  return value;
}

函数中会先推断当前要求的token是不是存在,假如不存在则去当前注入器的父注入器中寻觅,假如存在:

猎取token对应的record

推断record.value是不是为[](非useValue范例的效劳/多处置惩罚效劳的默认值是[]):

ture : 非useValue范例的效劳/多处置惩罚效劳或此效劳没有被建立过

  • 检察是不是包括依靠,包括则优先建立依靠的实例,也是挪用这个函数
  • 依据record.fn建立当前token对应的实例并更新record.value(这里须要依据record.useNew来推断是不是须要用new来实例化,比方useFactory范例就不须要new,而useExisting更是直接返回了deps)
  • 返回这个值

false : useValue范例的效劳或此效劳已被建立过

  • 直接返回这个值
    原文作者:chenqiao
    原文地址: https://segmentfault.com/a/1190000017382929
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞