上一篇說到了平臺實例在初始化的時候會創建根注入器,那現在就一起看看注入器是如何創建的,又是如何工作的.(所有引用的代碼都被簡化了)
創建注入器
程序初始化時調用的創建根注入器的靜態方法:
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
: 修改value
爲useValue
的值 -
useFactory
: 修改fn
爲對應的函數 -
useClass
或typeof provide == 'function'
(令牌爲一個函數時) : 修改fn
爲對應的函數,並設置useNew
爲true
-
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
中
從注入器中獲取實例
服務註冊完,下一步就是怎麼從注入器中獲取服務的實例了,這會調用StaticInjector
的get
方法
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
類型的服務或此服務已經被創建過
- 直接返回這個值