Angular 4.x Injector

semlinker發表於2019-02-28

檢視新版教程,請訪問前端修仙之路

在介紹 Angular Injector (注入器) 之前,我們先要了解 Dependency Injection,即依賴注入的概念。

軟體工程中,依賴注入是種實現控制反轉用於解決依賴性設計模式。一個依賴關係指的是可被利用的一種物件(即服務提供端) 。依賴注入是將所依賴的傳遞給將使用的從屬物件(即客戶端)。該服務是將會變成客戶端的狀態的一部分。 傳遞服務給客戶端,而非允許客戶端來建立或尋找服務,是本設計模式的基本要求。 —— 維基百科

依賴注入允許程式設計遵從依賴倒置原則 (簡單的說就是要求對抽象進行程式設計,不要對實現進行程式設計,這樣就降低了客戶端與實現模組間的耦合) 呼叫者只需知道服務的介面,具體服務的查詢和建立由注入器 (Injector) 負責處理並提供給呼叫者,這樣就分離了服務和呼叫者的依賴,符合低耦合的程式設計原則。

從上述的內容可知,依賴注入中包含三種角色:呼叫者、服務和注入器 (Injector)。現在我們開始介紹 Injector,在 Angular 中 Injector (注入器) 用來管理服務物件的建立和獲取。接下來我們先來看一下 Injector 抽象類:

Injector 抽象類

// angular2\packages\core\src\di\injector.ts
export abstract class Injector {
  static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
  static NULL: Injector = new _NullInjector();

  /**
   * 用於根據給定的Token從注入器中獲取相應的物件。
   * 如果沒有找到相應的物件,將返回notFoundValue設定的值。若notFoundValue的值與
   * _THROW_IF_NOT_FOUND相等,則會丟擲異常。
   */
  abstract get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
}

const _THROW_IF_NOT_FOUND = new Object();
複製程式碼

Injector 抽象類中定義了一個 get() 抽象方法,該方法用於根據給定的 Token 從注入器中獲取相應的物件,每個Injector 抽象類的子類都必須實現該方法。在 Angular 中常見的 Injector 抽象類子類有:

  • _NullInjector
  • ReflectiveInjector

下面我們來依次介紹它們:

_NullInjector 類

_NullInjector 類的例項用於表示空的注入器。

// angular2\packages\core\src\di\injector.ts
class _NullInjector implements Injector {
  get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
    if (notFoundValue === _THROW_IF_NOT_FOUND) {
      throw new Error(`No provider for ${stringify(token)}!`);
    }
    return notFoundValue;
  }
}
複製程式碼

ReflectiveInjector 抽象類

ReflectiveInjector 表示一個依賴注入容器,用於例項化物件和解析依賴。

ReflectiveInjector 使用示例

@Injectable()
class Engine {}

@Injectable()
class Car {
 constructor(public engine:Engine) {}
}

var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
var car = injector.get(Car);
expect(car instanceof Car).toBe(true);
expect(car.engine instanceof Engine).toBe(true);
複製程式碼

上面示例中,我們通過呼叫 ReflectiveInjector 抽象類的 resolveAndCreate() 方法,建立注入器。然後通過呼叫注入器的 get() 方法,獲取 Token 對應的物件。該抽象類除了 resolveAndCreate() 靜態方法外,還含有以下靜態方法:

  • resolve() - 解析 Provider 列表為 ResolvedReflectiveProvider 列表
  • fromResolvedProviders() - 基於 ResolvedReflectiveProvider 列表建立 ReflectiveInjector 物件

接下來我們來分析上述的靜態方法:

resolveAndCreate()

static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector {
    const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
    return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
}
複製程式碼

從上面程式碼中,我們可以看出 resolveAndCreate() 方法內部是通過呼叫 ReflectiveInjector.resolve() 方法和 ReflectiveInjector.fromResolvedProviders() 方法來建立 ReflectiveInjector 物件。

resolve()

該方法用於把 Provider 陣列解析為 ResolvedReflectiveProvider 陣列。

static resolve(providers: Provider[]): ResolvedReflectiveProvider[] {
    return resolveReflectiveProviders(providers);
}
複製程式碼

resolve() 使用示例

@Injectable()
class Engine {}

@Injectable()
class Car {
 constructor(public engine:Engine) {}
}

var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
expect(providers.length).toEqual(2);

expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
expect(providers[0].key.displayName).toBe("Car");
expect(providers[1].key.displayName).toBe("Engine");
複製程式碼

resolve() 解析圖示

Angular 4.x Injector

Provider 型別

export type Provider =
    TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];

// ApiService
export interface TypeProvider extends Type<any> {}

// { provide: ApiService, useClass: ApiService } 
export interface ClassProvider {
  // 用於設定與依賴物件關聯的Token值,Token值可能是Type、InjectionToken、OpaqueToken的例項或字串
  provide: any; 
  useClass: Type<any>;
  // 用於標識是否multiple providers,若是multiple型別,則返回與Token關聯的依賴物件列表
  multi?: boolean; 
}
  
// { provide: 'API_URL', useValue: 'http://my.api.com/v1' }
export interface ValueProvider {
  provide: any;
  useValue: any;
  multi?: boolean;
}
  
// { provide: 'ApiServiceAlias', useExisting: ApiService }  
export interface ExistingProvider {
  provide: any;
  useExisting: any;
  multi?: boolean;
}
  
// { provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], multi: true }
export interface FactoryProvider {
  provide: any;
  useFactory: Function;
  deps?: any[]; // 用於設定工廠函式的依賴物件
  multi?: boolean;
}
複製程式碼

ResolvedReflectiveProvider 介面

export interface ResolvedReflectiveProvider {
  // 唯一的物件用來從ReflectiveInjector中獲取物件
  key: ReflectiveKey;
 // 工廠函式用於建立key相關的依賴物件 
  resolvedFactories: ResolvedReflectiveFactory[];
  // 標識當前的provider是否為multi-provider
  multiProvider: boolean;
}
複製程式碼

ResolvedReflectiveFactory 類

export class ResolvedReflectiveFactory {
  constructor(
      public factory: Function,
      public dependencies: ReflectiveDependency[]) {}
}
複製程式碼

ReflectiveDependency 類

export class ReflectiveDependency {
  constructor(
      public key: ReflectiveKey, 
      public optional: boolean, 
      public visibility: Self|SkipSelf|null) {}

  static fromKey(key: ReflectiveKey): ReflectiveDependency {
    return new ReflectiveDependency(key, false, null);
  }
}
複製程式碼

ReflectiveKey 類

ReflectiveKey 物件中包含兩個屬性:系統範圍內唯一的id 和 token。系統範圍內唯一的id,允許注入器以更高效的方式儲存已建立的物件。另外我們不能手動的建立 ReflectiveKey,當 ReflectiveInjector 物件解析 providers 的時候會自動建立 ReflectiveKey 物件。

export class ReflectiveKey {
  constructor(public token: Object, public id: number) {
    if (!token) {
      throw new Error('Token must be defined!');
    }
  }
  
  // 返回序列化的token
  get displayName(): string { return stringify(this.token); }

  // 獲取token對應的ReflectiveKey
  static get(token: Object): ReflectiveKey {
    return _globalKeyRegistry.get(resolveForwardRef(token));
  }

  // 獲取系統中已註冊ReflectiveKey的個數
  static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; }
}

const _globalKeyRegistry = new KeyRegistry(); // 建立Key倉庫

export class KeyRegistry {
  private _allKeys = new Map<Object, ReflectiveKey>();

  /**
   * 若token是ReflectiveKey類的例項,則直接返回。若_allKeys物件中包含token屬性
   * 則返回token對應的ReflectiveKey物件。否則建立一個新的ReflectiveKey物件,並
   * 儲存到_allKeys物件中
   */
  get(token: Object): ReflectiveKey {
    if (token instanceof ReflectiveKey) return token;

    if (this._allKeys.has(token)) {
      return this._allKeys.get(token) !;
    }

    const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
    this._allKeys.set(token, newKey);
    return newKey;
  }

  // 獲取已儲存ReflectiveKey的個數
  get numberOfKeys(): number { return this._allKeys.size; }
}
複製程式碼

分析完 resolve() 方法的輸入引數和返回型別,我們來看一下該方法內部的具體實現:

export function resolveReflectiveProviders(providers: Provider[])
  : ResolvedReflectiveProvider[] {
    const normalized = _normalizeProviders(providers, []); // 步驟一
    const resolved = normalized.map(resolveReflectiveProvider); // 步驟二
    const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map()); // 步驟三
    return Array.from(resolvedProviderMap.values()); // 步驟四
}
複製程式碼

步驟一 —— 規範化Provider

const normalized = _normalizeProviders(providers, []); 

// 規範化Providers
function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[] {
  providers.forEach(b => {
    // providers: [Type] => providers: [{provide: Type, useClass: Type }]
    if (b instanceof Type) { 
      res.push({provide: b, useClass: b});
    } else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
      res.push(b as NormalizedProvider);
    } else if (b instanceof Array) { // 若b是陣列,則遞迴呼叫_normalizeProviders()方法
      _normalizeProviders(b, res);
    } else {
      throw invalidProviderError(b);
    }
  });
  return res;
}

interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider, 
   ExistingProvider, FactoryProvider {}
複製程式碼

步驟二 —— 轉化NormalizedProvider為ResolvedReflectiveProvider

const resolved = normalized.map(resolveReflectiveProvider);

// 解析NormalizedProvider為ResolvedReflectiveProvider
function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider {
  return new ResolvedReflectiveProvider_(
      ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)],
      provider.multi || false);
}

// 用於建立已解析的Provider例項
export class ResolvedReflectiveProvider_ implements ResolvedReflectiveProvider {
  constructor(
      public key: ReflectiveKey, 
      public resolvedFactories: ResolvedReflectiveFactory[],
      public multiProvider: boolean) {}

  get resolvedFactory(): ResolvedReflectiveFactory { return this.resolvedFactories[0]; }
}

// 解析NormalizedProvider物件,建立ResolvedReflectiveFactory物件
function resolveReflectiveFactory(provider: NormalizedProvider): ResolvedReflectiveFactory {
  let factoryFn: Function;
  let resolvedDeps: ReflectiveDependency[];
  if (provider.useClass) {
    // { provide: ApiService, useClass: ApiService } 
    const useClass = resolveForwardRef(provider.useClass);
    factoryFn = reflector.factory(useClass);
    resolvedDeps = _dependenciesFor(useClass);
  } else if (provider.useExisting) {
    // { provide: 'ApiServiceAlias', useExisting: ApiService } 
    factoryFn = (aliasInstance: any) => aliasInstance;
    resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
  } else if (provider.useFactory) {
    // { provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], 
    //     multi: true }
    factoryFn = provider.useFactory;
    resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
  } else {
    // { provide: 'API_URL', useValue: 'http://my.api.com/v1' }
    factoryFn = () => provider.useValue;
    // const _EMPTY_LIST: any[] = [];
    resolvedDeps = _EMPTY_LIST;
  }
  return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
}
複製程式碼

步驟三 —— 合併已解析的Provider

const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());

export function mergeResolvedReflectiveProviders(
    providers: ResolvedReflectiveProvider[],
    normalizedProvidersMap: Map<number, ResolvedReflectiveProvider>):
    Map<number, ResolvedReflectiveProvider> {
   for (let i = 0; i < providers.length; i++) {
    const provider = providers[i];
     // 從normalizedProvidersMap物件中獲取key.id對應的ResolvedReflectiveProvider物件
    const existing = normalizedProvidersMap.get(provider.key.id);
    if (existing) {
       // 如果當前的provider不是multi provider,則丟擲異常
      if (provider.multiProvider !== existing.multiProvider) {
        throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
      }
      // 如果當前的provider是multi provider,則把當前provider的resolvedFactories
      // 列表中的每一項新增到已存在的provider物件的resolvedFactories列表中。
      if (provider.multiProvider) {
        for (let j = 0; j < provider.resolvedFactories.length; j++) {
          existing.resolvedFactories.push(provider.resolvedFactories[j]);
        }
      } else { 
        // 如果當前的provider不是multi provider,則覆蓋已存在的provider
        normalizedProvidersMap.set(provider.key.id, provider);
      }
    } else {
      let resolvedProvider: ResolvedReflectiveProvider;
      // 如果當前的provider是multi provider,則建立一個新的ResolvedReflectiveProvider物件
      if (provider.multiProvider) {
        resolvedProvider = new ResolvedReflectiveProvider_(
            provider.key, provider.resolvedFactories.slice(), provider.multiProvider);
      } else {
        resolvedProvider = provider;
      }
      // 在normalizedProvidersMap中儲存已解析的ResolvedReflectiveProvider物件
      normalizedProvidersMap.set(provider.key.id, resolvedProvider);
    }
  }
  return normalizedProvidersMap;
}
複製程式碼

步驟四 —— 生成ResolvedReflectiveProvider[]

// resolvedProviderMap的values,建立ResolvedReflectiveProvider[]
Array.from(resolvedProviderMap.values());

/**
 * 基於一個類似陣列或可迭代物件建立一個新的陣列例項
 * 
 * arrayLike:轉換成真實陣列的類陣列物件或可遍歷物件。
 * mapFn(可選):如果指定了該引數,則最後生成的陣列會經過該函式的加工處理後再返回。
 * thisArg(可選):執行mapFn函式時this的值。
 */
Array.from(arrayLike[, mapFn[, thisArg]])
複製程式碼

fromResolvedProviders()

該方法用於基於已解析的 providers 建立注入器。

static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent?: Injector):
  ReflectiveInjector {
    return new ReflectiveInjector_(providers, parent);
}
複製程式碼

fromResolvedProviders() 使用示例

@Injectable()
class Engine {}

@Injectable()
class Car {
 constructor(public engine:Engine) {}
}

var providers = ReflectiveInjector.resolve([Car, Engine]);
var injector = ReflectiveInjector.fromResolvedProviders(providers);
expect(injector.get(Car) instanceof Car).toBe(true);
複製程式碼

瞭解完 fromResolvedProviders() 方法的使用方式,接下來我們來重點分析一下 ReflectiveInjector_ 類。

ReflectiveInjector_ 類

ReflectiveInjector_ 類的屬性

// 構造次數
_constructionCounter: number = 0;

// ResolvedReflectiveProvider列表
 public _providers: ResolvedReflectiveProvider[];

// 父級注入器
 public _parent: Injector|null;

// ReflectiveKey id列表
 keyIds: number[];

// 依賴物件列表
 objs: any[];
複製程式碼

ReflectiveInjector_ 建構函式

export class ReflectiveInjector_ implements ReflectiveInjector {
  constructor(_providers: ResolvedReflectiveProvider[], _parent?: Injector) {
      this._providers = _providers;
      // 設定父級注入器
      this._parent = _parent || null; 

      const len = _providers.length;

      this.keyIds = new Array(len);
      this.objs = new Array(len);

      // 初始化keyIds列表和objs物件列表
      for (let i = 0; i < len; i++) {
        this.keyIds[i] = _providers[i].key.id;
        this.objs[i] = UNDEFINED;
      }
  }
}

const UNDEFINED = new Object();
複製程式碼

ReflectiveInjector_ 類的方法

ReflectiveInjector_ 類中的方法較多,我們只分析其中比較重要的方法,首先先根據方法的實現的功能進行分類:

  • 用於建立ReflectiveInjector注入器
  • 用於獲取物件
  • 用於建立物件
  • 用於獲取工廠函式依賴物件

用於建立ReflectiveInjector注入器

// 基於Provider列表並建立子注入器
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
    const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
    return this.createChildFromResolved(ResolvedReflectiveProviders);
}

// 基於已解析的ResolvedReflectiveProvider列表,建立子注入器
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
    const inj = new ReflectiveInjector_(providers);
    inj._parent = this;
    return inj;
}
複製程式碼

用於獲取物件

// 獲取當前注入器的父級注入器
get parent(): Injector|null { return this._parent; }

// 獲取token對應的依賴物件
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
    return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
}

// 根據ReflectiveKey及visibility可見性,獲取對應的依賴物件
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf|null, notFoundValue: any): any {
    // const INJECTOR_KEY = ReflectiveKey.get(Injector); 
    if (key === INJECTOR_KEY) {
      return this;
    }

    // 判斷該依賴物件是否使用@Self裝飾器定義,表示從本級注入器獲取依賴物件
    if (visibility instanceof Self) {
      return this._getByKeySelf(key, notFoundValue);

    } else {
      // 使用預設的方式獲取依賴物件
      return this._getByKeyDefault(key, notFoundValue, visibility);
    }
}

// 從本級注入器獲取依賴物件
 _getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
    const obj = this._getObjByKeyId(key.id);
    return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
}

// 使用預設的方式獲取依賴物件
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, 
   visibility: Self|SkipSelf|null): any {
    let inj: Injector|null;

    // 判斷該依賴物件是否使用@SkipSelf裝飾器定義,表示不從本級注入器獲取依賴物件
    if (visibility instanceof SkipSelf) {
      inj = this._parent;
    } else {
      inj = this;
    }

    // 從本級注入器獲取依賴物件,若本級獲取不到,則從父級注入器中查詢
    while (inj instanceof ReflectiveInjector_) {
      const inj_ = <ReflectiveInjector_>inj;
      const obj = inj_._getObjByKeyId(key.id);
      if (obj !== UNDEFINED) return obj;
      inj = inj_._parent;
    }
    if (inj !== null) {
      return inj.get(key.token, notFoundValue);
    } else {
      return this._throwOrNull(key, notFoundValue);
    }
}

// 獲取keyId對應的物件,如依賴物件未建立,則呼叫_new()方法建立一個,然後儲存到
// this.objs物件列表中
private _getObjByKeyId(keyId: number): any {
    for (let i = 0; i < this.keyIds.length; i++) {
      if (this.keyIds[i] === keyId) {
        // const UNDEFINED = new Object();
        if (this.objs[i] === UNDEFINED) {
          this.objs[i] = this._new(this._providers[i]);
        }
        return this.objs[i];
      }
    }
    return UNDEFINED;
}
複製程式碼

用於建立物件

// 建立依賴物件
_new(provider: ResolvedReflectiveProvider): any {
   //  判斷是否存在迴圈依賴
    if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
      throw cyclicDependencyError(this, provider.key);
    }
    return this._instantiateProvider(provider);
}

// 獲取最大的物件個數
private _getMaxNumberOfObjects(): number { return this.objs.length; }

// 根據已解析的provider建立依賴物件。若是multi provider則,迴圈建立multi provider物件。 
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
    if (provider.multiProvider) {
      const res = new Array(provider.resolvedFactories.length);
      for (let i = 0; i < provider.resolvedFactories.length; ++i) {
        res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
      }
      return res;
    } else {
      return this._instantiate(provider, provider.resolvedFactories[0]);
    }
}

// 根據已解析的provider和已解析的工廠建立依賴物件
private _instantiate(
      provider: ResolvedReflectiveProvider,
      ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
    // 獲取物件工廠函式
    const factory = ResolvedReflectiveFactory.factory;

    // 獲取工廠函式所依賴的物件列表
    let deps: any[];
    try {
      deps = ResolvedReflectiveFactory.dependencies
        	  .map(dep => this._getByReflectiveDependency(dep));
    } catch (e) {
      if (e.addKey) {
        e.addKey(this, provider.key);
      }
      throw e;
    }

    // 呼叫物件工廠函式建立依賴物件
    let obj: any;
    try {
      obj = factory(...deps);
    } catch (e) {
      throw instantiationError(this, e, e.stack, provider.key);
    }
    return obj;
  }
複製程式碼

用於獲取工廠函式依賴物件

// 若通過@Optional裝飾器定義該依賴物件,表示該依賴物件是可選的,當獲取不到時返回null。
private _getByReflectiveDependency(dep: ReflectiveDependency): any {
    return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
}
複製程式碼

其實 Angular DI 最核心的內容是,如何建立依賴物件?在 Angular 中我們通過使用工廠函式,來建立依賴物件。工廠函式的輸入引數是依賴物件列表,輸出結果是對應的依賴物件。因此接下來我們將著重介紹如何建立工廠函式和如何建立依賴物件?但在開始介紹之前,我們還得先介紹一下 Angular Metadata 的相關內容。

Angular Metadata

在 Angular 中 Metadata 主要分為以下幾種型別:

  • annotations
  • design:paramtypes
  • propMetadata
  • parameters

接下來我們來看一下具體示例:

Angular 4.x Injector

友情提示:其中 design:paramtypes 和 parameters metadata 型別主要用於實現依賴注入

Angular 使用第三方庫 core-js 提供的 Reflect API ,來實現對 Metadata 資訊的儲存與讀取。

Metadata 資訊的儲存

我們以類裝飾器為例,如 @Component()@NgModule()

// angular2/packages/core/src/util/decorators.ts
export function makeDecorator(
    name: string, 
    props: {[name: string]: any}, 
    parentClass?: any,
    chainFn?: (fn: Function) => void): (...args: any[]) => (cls: any) => any {
 	//...
 	const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: 
 		Type<any>) {
      const annotations = Reflect.getOwnMetadata('annotations', cls) || [];
      annotations.push(annotationInstance);
      // 儲存annotations metadata資訊
      Reflect.defineMetadata('annotations', annotations, cls);
      return cls;
    };
}	
複製程式碼

Reflect.defineMetadata() 方法用來儲存定義的 Metadata 資訊,相應的 Metadata 資訊是儲存在 window['__core-js_shared__'] 物件的 metadata 屬性中。感興趣的話,大家可以直接在控制檯,輸入 window['__core-js_shared__'] 檢視該物件的內部資訊。介紹完 Metadata 資訊的儲存,我們來了解一下如何獲取 Metadata 資訊。

Angular 4.x Injector

Metadata 資訊的讀取

Angular 中通過 ReflectorReader 抽象類,定義了讀取 Metadata 資訊相關的抽象方法:

export abstract class ReflectorReader {
  abstract parameters(typeOrFunc: /*Type*/ any): any[][];
  abstract annotations(typeOrFunc: /*Type*/ any): any[];
  abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]};
  abstract importUri(typeOrFunc: /*Type*/ any): string|null;
  abstract resourceUri(typeOrFunc: /*Type*/ any): string;
  abstract resolveIdentifier(name: string, moduleUrl: string, members: string[], 
  	runtime: any): any;
  abstract resolveEnum(identifier: any, name: string): any;
}
複製程式碼

上述抽象方法的具體實現類是 - Reflector 類:

// angular2/packages/core/src/reflection/reflector.ts
export class Reflector extends ReflectorReader {
  constructor(public reflectionCapabilities: PlatformReflectionCapabilities) { 
    super(); 
  }
  //...
  factory(type: Type<any>): Function { return this.reflectionCapabilities.factory(type); }

  parameters(typeOrFunc: Type<any>): any[][] {
    return this.reflectionCapabilities.parameters(typeOrFunc);
  }

  annotations(typeOrFunc: Type<any>): any[] {
    return this.reflectionCapabilities.annotations(typeOrFunc);
  }

  propMetadata(typeOrFunc: Type<any>): {[key: string]: any[]} {
    return this.reflectionCapabilities.propMetadata(typeOrFunc);
  }
}
複製程式碼

介紹完 Angular Metadata 的相關知識,我們來開始介紹如何建立依賴物件。

建立依賴物件

建立ResolvedReflectiveFactory

// 解析NormalizedProvider物件,建立ResolvedReflectiveFactory物件
function resolveReflectiveFactory(provider: NormalizedProvider)
  : ResolvedReflectiveFactory {
    let factoryFn: Function;
    let resolvedDeps: ReflectiveDependency[];
    if (provider.useClass) {
      // { provide: ApiService, useClass: ApiService } 
      const useClass = resolveForwardRef(provider.useClass);
      factoryFn = reflector.factory(useClass);
      resolvedDeps = _dependenciesFor(useClass);
    } else if (provider.useExisting) {
      // { provide: 'ApiServiceAlias', useExisting: ApiService } 
      factoryFn = (aliasInstance: any) => aliasInstance;
      resolvedDeps = [ReflectiveDependency
        .fromKey(ReflectiveKey.get(provider.useExisting))];
    } else if (provider.useFactory) {
      // { provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], 
      //     multi: true }
      factoryFn = provider.useFactory;
      resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
    } else {
      // { provide: 'API_URL', useValue: 'http://my.api.com/v1' }
      factoryFn = () => provider.useValue;
      // const _EMPTY_LIST: any[] = [];
      resolvedDeps = _EMPTY_LIST;
    }
    return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
}
複製程式碼

useClass

// { provide: ApiService, useClass: ApiService } 
const useClass = resolveForwardRef(provider.useClass);
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
複製程式碼

設定工廠函式

// 獲取通過forwardRef()方法定義的類
const useClass = resolveForwardRef(provider.useClass);
factoryFn = reflector.factory(useClass);

// reflector.factory() 方法
factory<T>(t: Type<T>): (args: any[]) => T { 
  return (...args: any[]) => new t(...args); 
}
複製程式碼

設定依賴物件列表

// 解析類中的依賴物件
function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
  // 獲取design:paramtypes和paramters儲存的metadata資訊
  const params = reflector.parameters(typeOrFunc);
  if (!params) return [];
  if (params.some(p => p == null)) {
    throw noAnnotationError(typeOrFunc, params);
  }
  return params.map(p => _extractToken(typeOrFunc, p, params));
}

// 建立ReflectiveDependency物件
function _extractToken(typeOrFunc: any, metadata: any[] | any, 
   params: any[][]): 
  ReflectiveDependency {
  let token: any = null;
  let optional = false;

  if (!Array.isArray(metadata)) {
  // Inject: InjectDecorator = makeParamDecorator('Inject', [['token', 
  //   undefined]]);
    if (metadata instanceof Inject) {
      return _createDependency(metadata['token'], optional, null);
    } else {
      return _createDependency(metadata, optional, null);
    }
  }

  let visibility: Self|SkipSelf|null = null;
  
  // 遍歷metadata陣列,設定token、optional(可選的)、visibility(可見性)的值
  for (let i = 0; i < metadata.length; ++i) {
    const paramMetadata = metadata[i];

    if (paramMetadata instanceof Type) {
      token = paramMetadata;

    } else if (paramMetadata instanceof Inject) {
      token = paramMetadata['token'];

    } else if (paramMetadata instanceof Optional) {
      optional = true;

    } else if (paramMetadata instanceof Self || 
        paramMetadata instanceof SkipSelf) {
      visibility = paramMetadata;
    } else if (paramMetadata instanceof InjectionToken) {
      token = paramMetadata;
    }
  }

  // 獲取通過forwardRef()方法定義的類
  token = resolveForwardRef(token);

  if (token != null) {
    return _createDependency(token, optional, visibility);
  } else {
    throw noAnnotationError(typeOrFunc, params);
  }
}

// 建立ReflectiveDependency物件
function _createDependency(
  token: any, // 依賴物件關聯的token
  optional: boolean, // 是否是可選的
  visibility: Self | SkipSelf | null): ReflectiveDependency {
  	return new ReflectiveDependency(
      ReflectiveKey.get(token), 
      optional, 
      visibility);
}
複製程式碼

useExisting

// { provide: 'ApiServiceAlias', useExisting: ApiService } 
factoryFn = (aliasInstance: any) => aliasInstance;
resolvedDeps = [ReflectiveDependency
  .fromKey(ReflectiveKey.get(provider.useExisting))];
複製程式碼

設定工廠函式

factoryFn = (aliasInstance: any) => aliasInstance;
複製程式碼

設定依賴物件列表

// { provide: 'ApiServiceAlias', useExisting: ApiService } 
resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];

export class ReflectiveDependency {
  constructor(
    public key: ReflectiveKey, 
    public optional: boolean, 
    public visibility: Self|SkipSelf|null) {}
  
  static fromKey(key: ReflectiveKey): ReflectiveDependency {
    return new ReflectiveDependency(key, false, null);
  }
}
複製程式碼

useFactory

// { provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], 
//     multi: true }
factoryFn = provider.useFactory;
resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
複製程式碼

設定工廠函式

factoryFn = provider.useFactory;
複製程式碼

設定依賴物件列表

resolvedDeps = constructDependencies(provider.useFactory, provider.deps);

// 構造ReflectiveDependency[]列表
export function constructDependencies(
    typeOrFunc: any, dependencies?: any[]): ReflectiveDependency[] {
  if (!dependencies) {
    return _dependenciesFor(typeOrFunc);
  } else {
    const params: any[][] = dependencies.map(t => [t]);
    return dependencies.map(t => _extractToken(typeOrFunc, t, params));
  }
}
複製程式碼

useValue

// { provide: 'API_URL', useValue: 'http://my.api.com/v1' }
factoryFn = () => provider.useValue;
resolvedDeps = _EMPTY_LIST; // const _EMPTY_LIST: any[] = [];
複製程式碼

設定工廠函式

factoryFn = () => provider.useValue;
複製程式碼

設定依賴物件列表

resolvedDeps = _EMPTY_LIST; // const _EMPTY_LIST: any[] = [];
複製程式碼

現在 Angular Injector 的相關知識,已經介紹完了。由於涉及過多的原始碼,沒介紹清楚的地方,請見諒。

我有話說

AngularJS 1.x 依賴注入存在的問題

  • 內部快取:AngularJS 1.x 應用程式中所有的依賴項都是單例,我們不能控制是否使用新的例項
  • 名稱空間衝突: 在系統中我們使用字串來標識 service 的名稱,假設我們在專案中已有一個 CarService,然而第三方庫中也引入了同樣的服務,這樣的話就容易出現混淆
  • DI 耦合度太高: AngularJS 1.x 中 DI 功能已經被框架整合了,我們不能單獨使用它的 DI 特性
  • 未能和模組載入器結合: 在瀏覽器環境中,很多場景都是非同步的過程,我們需要的依賴模組並不是一開始就載入好的,或許我們在建立的時候才會去載入依賴模組,再進行依賴建立,而 AngularJS 1.x 的 DI 系統沒法做到這點。 

詳細的內容可以參考 - Angular 2 DI - IoC & DI - 1

Angular 注入器有什麼特點?

Angular 中注入器是有層級結構的,即建立完注入器,我們可以基於它建立它的子注入器。

  • resolveAndCreate() - 根據設定的 provider 陣列建立注入器
  • resolveAndCreateChild() - 呼叫已有注入器物件上的該方法,建立子注入器

當呼叫注入器 get() 方法,獲取 token 對應的物件時,預設的查詢方式是,優先從本級注入級獲取,如果未找到,則往上一級查詢,直到根級注入器。若未找到對應的依賴物件,預設會丟擲異常。

使用示例

class ParentProvider {}
class ChildProvider {}

var parent = ReflectiveInjector.resolveAndCreate([ParentProvider]);
var child = parent.resolveAndCreateChild([ChildProvider]);
expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
複製程式碼

通過分析原始碼,我們也發現如果兩個服務提供商 (Provider) 使用同一個 Token,卻沒有宣告為 multi provider,那麼後面的會把前面的覆蓋掉。此外需要注意的是,multi provider 不能與普通的 provider 混用。

@Self()、@SkipSelf()、@Optional() 等裝飾器有什麼作用?

  • @Self() - 表示只在本級注入器查詢依賴物件
  • @SkipSelf() - 表示不從本級注入器獲取依賴物件
  • @Optional - 表示該依賴物件是可選的,如果找不到返回 null

相關文章