Angular2 依賴注入之例項化過程
這裡會介紹 Angular 的注入器和例項化過程,基於 [email protected]
Angular 依賴注入過程主要有下面幾個重要的部分組成:
- Inject 和 Injectable 裝飾器
- 解析提供商,構造注入器
- 獲取例項化物件
Angular 依賴注入中的一些重要的概念:
Provider :
提供商,下面就是一個Proviver,一共有5種構造提供商的方式:TypeProvider, ValueProvider, ClassProvider, ExistingProvider, FactoryProvider
{ provide: Logger, useClass: Logger }
Token :
令牌,提供商中的第一個引數就是Token,在查詢依賴時作為 key 使用。
Injector :
注入器,用於解析依賴和建立物件。
Example
class Engine {
start() {
console.log('engine start');
}
}
class Car {
engine: Engine;
constructor(@Inject(Engine) engine) {
this.engine = engine;
}
open() {
this.engine.start();
console.log('car open' );
}
}
let inj = ReflectiveInjector.resolveAndCreate([
Car,
Engine
]);
let car = inj.get(Car);
car.open();
2. 解析服務提供商,構造注入器
在上面註解完成之後,接下來可以構造 Injector,Injector 首先需要解析 Provider.
let inj = ReflectiveInjector.resolveAndCreate([
{provide:Car,useClass:Car},
{provide:Engine,useClass:Engine}
]);
Provider 的解析主要由 resolveReflectiveProviders() 來完成,解析過程主要分為
- provider 引數預處理
- 解析例項化工廠及其依賴
- 合併提供商
- 構造注入器
下面是解析過程的原始碼
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());
}
2.1 引數預處理
_normalizeProviders 是將 provider 引數標準化,將省略了 useClass 的補全,將巢狀的 Provider 陣列拍平
function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[] {
providers.forEach(b => {
//如果 provider 是建構函式,對應沒有提供useClass的情況
if (b instanceof Type) {
res.push({provide: b, useClass: b});
//如果是物件,且帶provide屬性,直接放入陣列
} else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
res.push(b as NormalizedProvider);
//如果是陣列,遞迴拍平
} else if (b instanceof Array) {
_normalizeProviders(b, res);
} else {
throw invalidProviderError(b);
}
});
return res;
}
2.2 解析例項化工廠及其依賴
resolveReflectiveProvider 函式會根據 ReflectiveKey 和 ResolvedReflectiveFactory 返回一個 ResolvedReflectiveProvider_ 物件。
function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider {
return new ResolvedReflectiveProvider_(
ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)],
provider.multi || false);
}
其中的 resolveReflectiveFactory() 函式是其中的重點,解析的例項化工程和以來的方式有4鍾,分別對應 4種 Provider 的建立方式,分別是
- useClass
- useExisting
- useFactory
- useValue
這個函式裡面會根據這四種方式提供不同的例項化工廠和依賴解析,依賴解析完成會返回一個 ReflectiveDependency 陣列。
ReflectiveDependency 的建構函式為
constructor(
public key: ReflectiveKey, public optional: boolean,
public visibility: Self|SkipSelf|null)
ReflectiveKey 有兩個屬性: id 和 token ,其內部維持了一個Map
2.2.1 useClass
例項化工廠
useClass 的情況下例項化就通過 new 的方式完成
factoryFn = (...args: any[]) => new t(...args)
依賴解析方式
從 Inject 和 Injectable 兩個裝飾器中儲存的資訊尋找依賴的建構函式,也就是從 ‘parameters’ 和 ‘design:paramtypes’ 兩個元資料中尋找。如果當前建構函式沒有,回從原型鏈向上查詢
2.2.2 useExisting
例項化工廠
useExisting 情況下是一個返回傳入引數的函式,也就是返回依賴的例項
factoryFn = (aliasInstance: any) => aliasInstance;
依賴解析方式
將 useExisting 的值作為其依賴,也就是作為後面例項化時的引數
[ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))]
2.2.3 useFactory
例項化工廠
useFactory 的情況下就是useFactory的值
factoryFn = provider.useFactory;
依賴解析方式
如果定義了 deps,則直接從 deps 中抽取出 token 並構造ReflectiveDependency
否則使用和 useClass 相同的方法解析依賴
2.2.4 useValue
例項化工廠
直接返回這個值
factoryFn = () => provider.useValue;
依賴解析方式
空陣列
2.3 合併提供商
Angular 允許為一個 token 提供多個 provider, 只要申明 multi:true 即可。
let inj = ReflectiveInjector.resolveAndCreate([
{provide:Car,useClass:Car,multi:true},
{provide:Car,useClass:SuperCar,multi:true},
{provide:Engine,useClass:Engine}
]);
let car = inj.get(Car); //car = [Car, SuperCar]
此時使用 injector.get() 方法返回的是一個數組。 Angular 使用這種機制來提供可插拔的介面(pluggable hooks).
mergeResolvedReflectiveProviders() 函式就是用來處理多提供商的情況的,對申明 multi:true 的提供商,它會為一個 token 提供多個值。
2.4 構造注入器
ReflectiveInjector 的構造器如下,使用上面解析完成的 ResolvedReflectiveProvider_ 和父注入器例項化。
constructor(_providers: ResolvedReflectiveProvider[], _parent?: Injector)
3. 從注入器獲取物件
從注入器中獲取物件主要分為以下幾步:
- 判斷從哪個注入器獲取物件
- 從快取中查詢
- 例項化物件及其依賴
3.1 判斷 Self 和 SkipSelf
預設是如果當前注入器沒找到,則遞歸向父注入器查詢
self 只從當前注入器查詢
SkipSelf 從父注入器開始查詢
while (inj instanceof ReflectiveInjector_) {
const inj_ = <ReflectiveInjector_>inj;
const obj = inj_._getObjByKeyId(key.id);
if (obj !== UNDEFINED) return obj;
inj = inj_._parent;
}
3.2 快取
ReflectiveInjector_ 物件中有兩名兩個屬性,主要用於快取物件。 keyIds 是所有 ReflectiveKey 的 id, objs 是快取的已經例項化的物件。
keyIds: number[];
objs: any[];
根據 ReflectiveKey 的 Id 判斷是否已經例項化。 如果沒有就 new 一個。
private _getObjByKeyId(keyId: number): any {
for (let i = 0; i < this.keyIds.length; i++) {
if (this.keyIds[i] === keyId) {
if (this.objs[i] === UNDEFINED) {
//例項化物件,並快取到objs
this.objs[i] = this._new(this._providers[i]);
}
return this.objs[i];
}
}
return UNDEFINED;
}
3.3 例項化
例項化物件會首先例項化其需要的依賴,然後再根據由其例項化工廠來處理。
private _instantiate(
provider: ResolvedReflectiveProvider,
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
const factory = ResolvedReflectiveFactory.factory;
let deps: any[];
try {
//例項化所有依賴
deps =
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependen(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;
}
相關推薦
Angular2 依賴注入之例項化過程
這裡會介紹 Angular 的注入器和例項化過程,基於 [email protected] Angular 依賴注入過程主要有下面幾個重要的部分組成: Inject 和 Injectable 裝飾器 解析提供商,構造注入器 獲取例項化物件
[轉]Spring依賴注入和例項化(new)的差別
Spring依賴注入和例項化(new)的差別。 Sping為什麼使用依賴注入而不使用例項化物件的方式? 首先說明一下概念 依賴注入(Dependency of Injection)和控制反轉(Inversion of Control 簡稱
Angular2 依賴注入之裝飾器
下面會從 Angular 的原始碼分析依賴注入中的的裝飾器,基於 [email protected] Angular 依賴注入過程主要有下面幾個重要的部分組成: Inject 和 Injectable 裝飾器 解析提供商,構造注入器 獲取例項化物
依賴注入之Bean的例項化
上一篇分析到了通過遞迴的方式獲取Bean的所有依賴,接下來就該例項化Bean了。 對於例項化Bean,Spring提供了兩種方式,一種是Jdk的反射功能,還有一種就是Cglib。這兩種例項化的方式的區別是什麼呢? 依賴注入的起點是getBean方法,然後
springboot bean的例項化過程和屬性注入過程
瞭解過springboot的載入流程的都知道springboot初始化bean都在refresh方法中。這個方法程式碼如下:// Prepare this context for refreshing. prepareRefresh(); // Tell the subcla
java之面向物件:子類物件的例項化過程詳解
在子類建構函式中,發現,訪問子類建構函式時,父類也運行了。原因是什麼呢? 在子類的建構函式裡第一行有一個預設的隱式語句:super() ExtendsDemo.java class Fu { Fu() { System.out.println("fu run")
spring源碼分析之初始化過程
源碼分析 true singleton 存在 factory 源碼 org 包含 eric 1.org.springframework.web.context.ContextLoaderListener 一個ServletContextListener,web容器啟動監聽器
面向物件【day07】:類的例項化過程剖析(三)
本節內容 1、概述 2、類的語法 3、總結 一、概述 之前我們說關於python中的類,都一臉懵逼,都想說,類這麼牛逼到底是什麼,什麼才是類?下面我們就來講講,什麼是類?它具有哪些特性。 二、類的語法 2.1 語法
JVM總括四-類載入過程、雙親委派模型、物件例項化過程 JVM思考-init和clinit區別
JVM總括四-類載入過程、雙親委派模型、物件例項化過程 目錄:JVM總括:目錄 一、 類載入過程 類載入過程就是將.class檔案轉化為Class物件,類例項化的過程,(User user = new User(); 這個過程是物件例項化的過程); 一個.class檔案只有一個Class物件(位元
JVM思考-init和clinit區別 JVM總括四-類載入過程、雙親委派模型、物件例項化過程
JVM思考-init和clinit區別 目錄:JVM總括:目錄 clinit和init的區別其實也就是Class物件初始化物件初始化的區別,詳情看我上一篇部落格: JVM總括四-類載入過程、雙親委派模型、物件例項化過程 一、init和clinit方法執行時機不同 init是物件構
JVM總括四-類載入過程、雙親委派模型、物件例項化過程
JVM總括四-類載入過程、雙親委派模型、物件例項化過程 目錄:JVM總括:目錄 一、 類載入過程 類載入過程就是將.class檔案轉化為Class物件, 類例項化 的過程 ,(User user = new User(); 這個過程是 物件例項化 的
sql注入與防止SQL注入之引數化處理
sql注入的兩種情況: 操作程式碼: import pymysql user = input('使用者名稱: ').strip() pwd = input('密碼: ').strip() # 連結 conn = pymysql.connect(host='localhost', user='ro
啟動期間的記憶體管理之初始化過程概述----Linux記憶體管理(九)
在記憶體管理的上下文中, 初始化(initialization)可以有多種含義. 在許多CPU上, 必須顯式設定適用於Linux核心的記憶體模型. 例如在x86_32上需要切換到保護模式, 然後核心才能檢測到可用記憶體和暫存器. 而我們今天要講的boot階段
類載入過程(clinit()),物件例項化過程(init())
類載入過程: 1、類載入就是執行Java程式編譯之後在位元組碼檔案中生成的clinit()方法(稱之為類構造器),clinit()方法由靜態變數和靜態程式碼塊組成。 2、子類的載入首先需要先載入父類,如果父類為介面。則不會呼叫父類的clinit方法。一個類中可以沒有clinit方法。 3、clinit方
spring原始碼閱讀(1)- ioc依賴注入之bean載入
還是先看下DefaultListableBeanFactory的類結構圖 我們從User user = (User) beanFactory.getBean("user");入手進入bean的載入管理流程。 這裡還是堅持走主線的流程,去掉無關的枝葉,儘量讓業務變得簡
子類物件例項化過程
2018年11月05日 08:58:03 aprildusk 閱讀數:3 個人分類: java
tomcat原始碼解讀之初始化過程
之前我拜讀了《How Tomcat Works》這本書,對tomcat的架構與裡面的實現有了一定的瞭解,現在藉著tomcat的原始碼來深入瞭解這個精巧的藝術品。 首先從初始化開始, getServer().init(); 前面類載入的過程與digester讀
Java子父類中的建構函式例項化過程
其實我們發現子類繼承父類操作很簡單,如果要是去深入的研究下會發現,例項化過程並非是我們看到的那樣,我們就以程式碼舉例來說明; 問大家,以下程式碼執行會輸出什麼呢? package com.oop; /** * 定義動物物件 * @author
spring裝配Bean過程以及Bean例項化過程
<ol class="linenums" style="margin:0px; word-wrap:break-word; padding:0px 0px 0px 40px"><li class="L0" style="word-wrap:break-word; padding-left:
Java依賴注入(DI)例項詳解
Java依賴注入模式允許我們擺脫硬編碼,使我們的應用更加鬆耦合、增強擴充套件性以及可維護性。通過依賴注入我們可以降低從編譯到執行時的依賴性。 Java依賴注入 Java的依賴注入僅僅通過理論是很難解明白的,所以我們通過幾個簡單的示例來描述它,怎樣利用依賴