1. 程式人生 > 其它 >SAP Spartacus 中的依賴注入 Dependency Injection 介紹

SAP Spartacus 中的依賴注入 Dependency Injection 介紹

先了解 Angular 中的依賴注入

依賴項是指某個類執行其功能所需的服務或物件。依賴項注入(DI)是一種設計模式,在這種設計模式中,類會從外部源請求依賴項而不是讓類自己來建立它們。
Angular 的 DI 框架會在例項化某個類時為其提供依賴。你可以使用 Angular DI 來提高應用程式的靈活性和模組化程度。

如何建立一個新的可以被注入的 service ?

命令列:ng generate service heroes/hero

自動生成的程式碼,注意註解 @Injectable:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class HeroService {
  constructor() { }
}

@Injectable() 裝飾器會指定 Angular 可以在 DI 體系中使用此類。元資料 providedIn: 'root' 表示 HeroService 在整個應用程式中都是可見的。

配置提供者

通過配置提供者,你可以把服務提供給那些需要它們的應用部件。

依賴提供者會使用 DI 令牌來配置注入器,注入器會用它來提供這個依賴值的具體的、執行時版本。

如果你把服務類指定為提供者令牌,那麼注入器的預設行為是用 new 來例項化那個類。

在下面這個例子中,Logger 類提供了 Logger 的例項。

providers: [Logger]

當使用提供者配置注入器時,會將該提供者與依賴項注入令牌(或叫 DI 令牌)關聯起來。注入器允許 Angular 建立任何內部依賴項的對映。DI 令牌會充當該對映的鍵名。

當你使用 HeroService 類的型別來定義建構函式引數時,Angular 會注入與這個 HeroService 類令牌相關聯的服務:

constructor(heroService: HeroService)

這裡建構函式引數 heroService 實際上是一個令牌。

這個配置:

providers: [Logger]

實際上是下面寫法的簡寫:

[{ provide: Logger, useClass: Logger }]
  • provide 屬性存有令牌,它作為一個 key,在定位依賴值和配置注入器時使用。
  • 第二個屬性是一個提供者定義物件,它告訴注入器要如何建立依賴值。 提供者定義物件中的 key 可以是 useClass —— 就像這個例子中一樣。 也可以是 useExisting、useValue 或 useFactory。 每一個 key 都用於提供一種不同型別的依賴。

不同的類可以提供相同的服務。例如,以下程式碼告訴注入器,當元件使用 Logger 令牌請求一個 logger 時,給它返回一個 BetterLogger.

這樣,當我們的應用程式,在 constructor 裡使用下面程式碼試圖注入 Logger 時:

constructor(logger: Logger)

執行時我們拿到的就是 BetterLogger 例項。

injection token 的使用方法

  1. 使用 TypeScript interface 定義一個 AppConfig 型別(略)

  2. 基於 AppConfig 型別建立一個常量:

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com',
  title: 'Dependency Injection'
};
  1. 要提供並注入配置物件,請在 @NgModule() 的 providers 陣列中指定該物件。
providers: [
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],

上面程式碼裡 provide 屬性 APP_CONFIG 就是一個令牌,我們還需要使用程式碼建立這個令牌。

import { InjectionToken } from '@angular/core';

export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');

其中AppConfig 通過型別引數傳入令牌建構函式裡。app.config 是令牌描述資訊。

  1. 最後一步,在應用程式的建構函式裡,用 @Inject,注入這個令牌。執行時,config 的值即為令牌關聯的常量:HERO_DI_CONFIG
constructor(@Inject(APP_CONFIG) config: AppConfig) {
  this.title = config.title;
}

為什麼我們不能直接把第一步用 interface 建立的 AppConfig 作為 provide 的值,而要大費周章,建立一個 InjectionToken 呢?

雖然 TypeScript 的 AppConfig 介面可以在類中提供型別支援,但它在依賴注入時卻沒有任何作用。在 TypeScript 中,介面是一項設計期工件,它沒有可供 DI 框架使用的執行時表示形式或令牌。
當轉譯器把 TypeScript 轉換成 JavaScript 時,介面就會消失,因為 JavaScript 沒有介面。
由於 Angular 在執行期沒有介面,所以該介面不能作為令牌,也不能注入它。

因此下列程式碼不能工作:

// Can't use interface as provider token
[{ provide: AppConfig, useValue: HERO_DI_CONFIG })]

看一個 SAP Spartacus 中 Injection Token 的使用例子:

export const PAGE_LAYOUT_HANDLER = new InjectionToken<PageLayoutHandler[]>(
  'PageLayoutHandler'
);

在 Cart.module.ts 裡使用到了這個令牌:

最後在 PageLayoutService 裡使用到了這個令牌指向的服務例項:PageLayoutHandler 組成的陣列。

更多Jerry的原創文章,盡在:"汪子熙":