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 类
在构造函数中做了几件重要事情:
- 接收
parent
注入器 - 定义一个空的
records
的Map<any, Record | null>
- 初始化层级
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
(一个普通的构造函数)
此函数主要干这么几件事情:
- 解析
provider
的deps
[computeDeps
] - 找出
provider
的具体类型,如:useClass, useValue, useFactory ...
- 用
value
记录useValue
- 用
fn
记录useFactory
,useClass
,function
-
useExisting
在这什么也不干 - 返回解析出来带得有依赖的一条记录
record
,{deps, fn, useNew, value}
其中 useClass
, function
的 useNew
为 true
. 如果 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
相关的东西会在后面一一揭晓.
在这个函数中主要干这么几件事情:
- 用
flags
辅助判断各种配型的配置,如:SkipSelf
,Self
,Optional
,Default
- 根据
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
}
网友评论