最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

低配 Spring—Golang IOC 框架 dig 原理解析

2023-07-08 11:28 作者:清澄秋爽  | 我要投稿

0 前言

本期分享的主題是 Golang 中的 IOC 框架 dig,內(nèi)容涉及到個人對編程風(fēng)格的理解、對 dig 使用方法的介紹以及對 dig 底層原理的剖析.

原文:?https://mp.weixin.qq.com/s/bireIkWWTQUdgc-UJEhN5g


文章內(nèi)容的目錄樹結(jié)構(gòu)如下圖:

圖片

?

1 IOC 框架使用背景

在引入 IOC 概念之前,我們需要先補(bǔ)充一些前置設(shè)定:這里主要針對“面向?qū)ο缶幊獭焙汀俺蓡T依賴注入”兩個問題進(jìn)行探討.

1.1 面向?qū)ο缶幊?/h1>

首先拋出一個經(jīng)典問題:“面向?qū)ο蠛兔嫦蜻^程有什么區(qū)別?”

這是個抽象的問題,本質(zhì)上可以劃分到哲學(xué)的范疇,涉及到個人看待世界的角度.

我是個俗人,不太會聊哲學(xué),但是代碼領(lǐng)域的問題,我挺能聊.

下面,我們就化抽象為具象,嘗試用代碼實現(xiàn)一個場景——“把一只大象裝進(jìn)冰箱”.

圖片

?

在面向過程編程的視角下:

解決問題的核心是化整為零,把大問題拆解為一個個小問題,再針對小問題進(jìn)行逐個擊破.

在執(zhí)行綱領(lǐng)的指導(dǎo)下,我們在編寫代碼時需要注重的是步驟的拆分與流程的串聯(lián).

下面展示一下偽代碼:

?

與面向過程相對,在面向?qū)ο缶幊痰囊暯侵拢?/p>

一切皆為對象.

在本場景中,我選擇把大象和冰箱都看成是有靈魂的角色,并且準(zhǔn)備在交互場景中給予它們更多的參與感.

于是,這里首先塑造出大象和冰箱這兩種角色(聲明對象類);其次再給對應(yīng)的角色注入靈魂(賦予屬性和方法);最后,把主動權(quán)交還給各個角色,由它們完成場景下的互動:

?

(1)構(gòu)造對象/注入靈魂

就以大象裝冰箱的場景為例,我們首先我們構(gòu)造出大象和冰箱兩個對象,并賦予其對應(yīng)的能力,比如:

  • ??大象是有生命的,它會有自己的情緒,會有行動的能力;

  • ??冰箱作為容器,除了一些基本信息之外,最重要是具有裝載事物的能力.

?

?

(2)由對象完成交互

接下來,在場景的描述中,我們首先構(gòu)造出參與其中的各個對象,然后通過各對象本身固有的能力完成交互.

通過上述例子,希望能幫助大家對面向?qū)ο蟮木幊陶軐W(xué)產(chǎn)生更直觀的感受.

?

1.2 成員依賴注入

在日常業(yè)務(wù)代碼的編寫中,我個人會比較推崇面向?qū)ο蟮拇a風(fēng)格,原因如下:

  • ??面向?qū)ο缶幊叹哂蟹庋b、多態(tài)、繼承的能力,有利于系統(tǒng)模塊之間的聯(lián)動

  • ??將系統(tǒng)各模塊劃分為一個個對象,劃分邊界、各司其職,使得系統(tǒng)架構(gòu)層級分明、邊界清晰

  • ??對象中的核心成員屬性能夠被復(fù)用,避免重復(fù)構(gòu)造

?

上述的第三點需要基于面向?qū)ο缶幊?成員依賴注入的代碼風(fēng)格加以保證.

成員依賴注入是我在依賴注入Dependency Injection(DI)概念的基礎(chǔ)上小小調(diào)整后得到的復(fù)合概念,其指的是,在程序運行過程中,當(dāng)對象A需要依賴于另一個對象B協(xié)助完成工作時,不會在代碼中臨時創(chuàng)建對象B的實例,而是遵循以下幾個步驟:

  • ??前置將對象B聲明為對象A的成員屬性

  • ??在初始化對象A的構(gòu)造函數(shù)暴露出A對B的依賴

  • ??于是在A初始化前,先壓棧完成B的初始化工作

  • ??將初始化后的B注入A的構(gòu)造函數(shù),完成A的初始化

  • ??在A的生命周期內(nèi),完成對成員屬性B的復(fù)用,不再重復(fù)創(chuàng)建

?

下面進(jìn)一步舉正反例子,對比成員依賴注入這一思想對代碼風(fēng)格帶來的影響:

背景中,我們有三個對象,分別是:

  • ??和數(shù)據(jù)源打交道的 DAO

  • ??和第三方服務(wù)通信交互的 Client

  • ??聚集了核心業(yè)務(wù)流程的 Service,且 Service 會依賴于 DAO 和 Client 的能力

?

(1)無成員依賴注入

首先給出不遵循成員依賴注入的代碼反例:

?

在上述代碼中,存在的兩個局限性在于:

  • ??dao、client 等核心組件的生命周期局限于一個業(yè)務(wù)方法中,因此會被重復(fù)創(chuàng)建. 這類組件內(nèi)部本身還有依賴,其初始化過程通常是比較”重“的. 因此其多次重復(fù)創(chuàng)建/銷毀的行為可能會帶來嚴(yán)重的性能損耗

  • ??Service 與 dao、client 強(qiáng)耦合,模塊定位喪失靈活度. 這一點目前看來說得不夠直觀,可以相較第(2)部分來看

?

(2)遵循成員依賴注入

?

?

這種成員依賴注入風(fēng)格的代碼具有的特點包括:

  • ??依賴的核心組件一次注入,永久復(fù)用,沒有重復(fù)創(chuàng)建所帶來的成本

  • ??就近將成員抽象為 interface 后,基于多態(tài)的思路,Service 本身的定位更加靈活,取決于注入的成員變量的具體實現(xiàn)

圖片

舉例說明,把 dao 和 client 定義為 interface 后,

  • ??當(dāng)注入和食物數(shù)據(jù)庫交互的 foodDAO 和食物服務(wù)交互的 foodClient 時,service 就被定位成處理食物業(yè)務(wù)的模塊

  • ??當(dāng)注入和飲品數(shù)據(jù)庫交互的 drinkDAO 和飲品服務(wù)交互的 drinkClient 時,service 就被定位成處理飲品業(yè)務(wù)的模塊

  • ??...

?

foodClient + foodDAO -> foodSerivce

drinkClient + drinkDAO -> drinkSerivce

...

?

更進(jìn)一步,倘若我們需要編寫模塊的單測代碼,還可以實現(xiàn) mock 成員變量的注入,從而實現(xiàn)外置依賴的代碼邏輯的打樁,讓單測邏輯能夠好地聚焦在 Service 領(lǐng)域的業(yè)務(wù)代碼:

  • ??當(dāng)注入 mockDAO 和 mockClient 時,service 就被成為了一個僅用于測試的 mock 業(yè)務(wù)模塊.

?

?

1.3 引入 IOC 的原因

在 1.2 小節(jié)的基礎(chǔ)上做個延伸性的探討,倘若所有代碼都嚴(yán)格遵循這種成員依賴注入的風(fēng)格,一旦系統(tǒng)架構(gòu)變得復(fù)雜,就會有新的問題產(chǎn)生:

(1)大量的依賴對象

圖片

?

倘若對象A依賴的成員模塊數(shù)量很大,每個成員都需要由構(gòu)造器的調(diào)用方通過入?yún)⑦M(jìn)行顯式注入,這樣編寫起來代碼復(fù)雜度過高:

?

(2)重復(fù)的依賴對象

圖片

?

此外,依賴路徑可能存在交叉的情況,最終形成一張錯綜復(fù)雜的依賴網(wǎng),此時就會產(chǎn)生兩個問題:

  • ??倘若某個子對象被多個父對象所依賴,如何保證子對象維持為單例狀態(tài),能夠被全局復(fù)用

  • ??如何梳理好復(fù)雜的依賴路徑,保證依賴注入流程的正常執(zhí)行

?

舉個代碼示例如下:

?

梳理完上述問題后,我們的訴求也逐漸清晰:

  • ??需要有一個全局的容器,實現(xiàn)對各個組件進(jìn)行緩存復(fù)用

  • ??需要有一個全局管理對象,為我們梳理各對象間的依賴路徑,依次完成依賴注入的工作

而本文的主題—— IOC 框架,扮演的正是這樣一個角色.

IOC,全稱 Inversion of Control 控制反轉(zhuǎn),指的是將業(yè)務(wù)組件的創(chuàng)建、復(fù)制、管理工作委托給業(yè)務(wù)代碼之外的容器進(jìn)行統(tǒng)一管理. 我們通常把容器稱為 container,把各個業(yè)務(wù)組件稱為 bean.

由于各個 bean 組件之間可能還存在依賴關(guān)系,因此 container 的另一項能力就是在需要構(gòu)建 bean 時,自動梳理出最優(yōu)的依賴路徑,依次完成依賴項的創(chuàng)建工作,最終產(chǎn)出用戶所需要的 bean.

在這個依賴路徑梳理的過程中,倘若 container 發(fā)現(xiàn)存在組件缺失,導(dǎo)致 bean 的依賴路徑無法達(dá)成,則會拋出錯誤終止流程. 通常這個流程會在編譯階段或者程序啟動之初執(zhí)行,因此倘若依賴項存在缺失,也能做到盡早拋錯、及時止損,引導(dǎo)開發(fā)人員提前解決代碼問題.

?

1.4 Golang IOC 框架 dig

(1)dig 基本信息

聊到 IOC 框架,JAVA 中的 Spring 是一座繞不過的大山. 相對于生態(tài)成熟資源豐富的 JAVA 而言,Golang 中成熟可用的 IOC 框架就相對有限.

而今天我們要介紹的主角是由 uber 開源的 dig,git開源地址為:https://github.com/uber-go/dig,本文走讀的源碼版本為 tag v1.15.

?

圖片

?

(2)dig 與 spring 的差距

dig 能夠為研發(fā)人員提供到前文提及的兩項核心能力:

  • ??bean 單例管理

  • ??bean 依賴路徑梳理

同時,本著實事求是的態(tài)度,我們也如實闡述一下 dig 相比于 spring 所缺失的能力:

(1)只有 IOC,不具有 AOP (Aspect Oriented Programming)的能力

(2)在同一個 key 下(bean type + bean name/group)只支持單例,不支持原型

(3)將 bean 注入 container 的方式相對單調(diào),強(qiáng)依賴于構(gòu)造器函數(shù)的模式

(4)由于依賴于構(gòu)造器函數(shù),因此不能解決循環(huán)依賴問題(事實上,在Golang 中,本就不支持循環(huán)依賴的模式,跨包之間的循環(huán)依賴引用,會在編譯層面報錯)

(5)bean 沒有支持豐富的生命周期方法

?

2 dig 使用教程

2.1 provide/invoke

圖片

?

首先給出代碼示例,供大家更直觀地感受通過 dig 實現(xiàn)依賴注入、路徑梳理、bean 復(fù)用的能力:

  • ??存在 bean A、bean B,其中 bean A 依賴于 bean B

  • ??聲明 bean A 和 bean B 的構(gòu)造器方法,A 對 B 的依賴關(guān)系需要在構(gòu)造器函數(shù) NewA 的入?yún)⒅畜w現(xiàn)

  • ??通過 dig.New 方法創(chuàng)建一個 dig container

  • ??通過 container.Provide 方法,分別往容器中傳入 A 和 B 的構(gòu)造器函數(shù)

  • ??同歸 container.Invoke 方法,傳入 bean A 的獲取器方法 func(_a *A),其中需要將獲取器函數(shù)的入?yún)㈩愋驮O(shè)置為 bean A 的類型

  • ??在獲取器方法運行過程中,入?yún)⑼ㄟ^容器取得 bean A 實例,此時可以通過閉包的方式將 bean A 導(dǎo)出到方法外層

?

輸出結(jié)果:

?

2.2 dig.In

2.1 小節(jié)介紹的基本用法中,我們需要將 bean A 依賴的子 bean 統(tǒng)統(tǒng)在構(gòu)造器函數(shù)中通過入?yún)⒌姆绞竭M(jìn)行聲明,倘若依賴數(shù)量較大的話,在聲明構(gòu)造器函數(shù)時可能存在不便,此時可以通過內(nèi)置 dig.In 標(biāo)識的方式替代構(gòu)造函數(shù),標(biāo)志出 A 中所有可導(dǎo)出的成員變量均為依賴項.

dig.In 方式的使用示例如下,其中需要注意的點是:

  • ??作為依賴 bean 的成員字段需要聲明為可導(dǎo)出類型

  • ??內(nèi)置了 dig.In 標(biāo)識的 bean,在通過 Invoke 流程與 container 交互時必須使用 struct 類型,不能使用 pointer 形式

?

輸出結(jié)果:

?

2.3 dig.Out

與 2.2 小節(jié)中的 dig.In 對偶,我們可以通過 dig.Out 聲明,在 Provide 流程中將某個類的所有可導(dǎo)出成員屬性均作為 bean 注入到 container 中.

與 dig.In 相仿,dig.Out 在使用時同樣有兩個注意點:

其中需要注意的點是:

  • ??需要作為注入 bean 的成員字段需要聲明為可導(dǎo)出類型

  • ??內(nèi)置了 dig.Out 標(biāo)識的 bean,在通過 Provide 流程與 container 交互時必須使用 struct 類型,不能使用 pointer 形式

?

?

輸出結(jié)果:

?

2.4 bean name

此外,倘若存同種類型存在多個不同的 bean 實例,上層需要進(jìn)行區(qū)分使用,此時 container 要如何進(jìn)行標(biāo)識和管理呢,答案就是通過 name 標(biāo)簽對 bean 進(jìn)行標(biāo)記,示例代碼如下:

?

輸出結(jié)果:

?

2.5 bean group

倘若依賴的是 bean list 該如何處理,這就需要用到 dig 中的 group 標(biāo)簽.

需要注意的點是,在通過內(nèi)置 dig.Out 的方式注入 bean list 的時候,需要在 group tag 中聲明 flatten 標(biāo)志,避免 group 標(biāo)識本身會將 bean 字段上升一個維度.

?

?

3 dig 原理解析

下面明確一下 dig 框架的實現(xiàn)原理,首先拆解一下宏觀流程中的要點:

  • ??基于注入構(gòu)造函數(shù)的方式,實現(xiàn) bean 的創(chuàng)建

  • ??基于反射的方式,實現(xiàn) bean 類型到到構(gòu)造函數(shù)的映射

  • ??在運行時而非編譯時實現(xiàn) bean 的依賴路徑梳理

?

在 dig 的實現(xiàn)中,bean 依賴路徑的梳理時機(jī)是在服務(wù)運行階段而非編譯階段,因此這個流程應(yīng)該和業(yè)務(wù)代碼解耦,專門聲明一個 factory 模塊聚合處理的 bean 的創(chuàng)建工作. 避免將 bean 獲取操作零星散落在業(yè)務(wù)流程各處,這樣倘若某個 bean 存在依賴缺失,則會導(dǎo)致服務(wù) panic.

?

3.1 核心數(shù)據(jù)結(jié)構(gòu)

在方法鏈路的源碼走讀和原理解析之前,先對 dig 中幾個重要的數(shù)據(jù)結(jié)構(gòu)進(jìn)行介紹:

圖片

?

(1)Container&Scope

Container 即存放和管理 bean 的全局容器.

Scope 是一個范圍塊,本質(zhì)上是一棵多叉樹中的一個節(jié)點,擁有自己的父節(jié)點和子節(jié)點列表.

一個 Container 由一棵 Scope 多叉樹構(gòu)成,手中持有的是 root Scope 的引用.

目前在筆者的工程實踐中未涉及到對 Scope 的使用,通常只使用一個 root Scope 就足以滿足完使用訴求.

因此,在本文的介紹中,大家可以簡單地把 Container 和 Scope 認(rèn)為是等效的概念.

?

?

?

(2)key

key 是容器中的唯一標(biāo)識鍵,由一個二元組構(gòu)成. 其中一維是 bean 的類型 reflect.Type,另一維是 bean 名稱 name 或者 bean 組名 group.

此處 name 字段和 group 字段是互斥關(guān)系,二者只會取其一. 因為一個 bean 被 provide 的時候,就會明確其是 single 類型還是 group 類型.

?

(3)constructorNode

圖片

?

constructorNode 是構(gòu)造器函數(shù)的封裝節(jié)點,包含的核心字段包括:

  • ??ctor:bean 構(gòu)造器函數(shù)

  • ??ctype:bean 構(gòu)造器函數(shù)類型

  • ??called:構(gòu)造器函數(shù)是否已被執(zhí)行過

  • ??paramList:構(gòu)造器函數(shù)依賴的入?yún)?/p>

  • ??resultList:構(gòu)造器函數(shù)產(chǎn)生的出參

?

?

(4)param

paramList 是構(gòu)造器節(jié)點的入?yún)⒘斜恚?/p>

  • ??ctype:構(gòu)造器函數(shù)的類型

  • ??params:入?yún)⒘斜?/p>

?

入?yún)?param 本身是個 interface,核心方法是 Build,邏輯是從存儲介質(zhì)(容器) containerStore 中提取出對應(yīng)于當(dāng)前 param 的 bean,然后通過響應(yīng)參數(shù)返回其 reflect.Value.

?

param 的實現(xiàn)類包括:

單個實體 bean,除了我們內(nèi)置 dig.In 標(biāo)識和通過 group 標(biāo)簽標(biāo)識的情況,其他的入?yún)?bean 都屬于 paramSingle 的形式.

?

通過 group 標(biāo)簽標(biāo)識的 bean group

?

內(nèi)置了 dig.In 的 bean

?

(5)result

resultList 是構(gòu)造器函數(shù)節(jié)點的出參列表:

  • ??ctype:構(gòu)造器函數(shù)的類型

  • ??Results:出參列表

?

出參 result 本身是個 interface,核心方法是 Exact,方法邏輯是將已取得的 bean reflect.Value 填充到容器 containerWriter 的緩存 map values 當(dāng)中.

?

result 的實現(xiàn)類包括:

單個實體 bean,除了我們內(nèi)置 dig.Out 標(biāo)識和通過 group 標(biāo)簽標(biāo)識的情況,其他的出參 bean 都屬于 resultSingle 的形式.

?

基于 group 標(biāo)簽標(biāo)識的 bean group

?

內(nèi)置了 dig.out 的 bean.

?

?

3.2 構(gòu)造全局容器

圖片

?

(1)dig.New

創(chuàng)建 dig 容器通過 dig.New 方法執(zhí)行,方法中會創(chuàng)建一個 Container 實例,并創(chuàng)建一個 rootScope 注入其中.

?

(2)dig.newCope

newScope 方法中創(chuàng)建了一個 Scope 實例,對 Scope 數(shù)據(jù)結(jié)構(gòu)中的幾個 map 成員變量進(jìn)行了初始化.

值得一提的是,此處聲明了獲取bean 的入口函數(shù) invokerFn 為 defaultInvoker. 其核心邏輯我們在 3.4 小節(jié)第(6)部分展開介紹.

?

?

3.3 注入 bean 構(gòu)造器

圖片

在 dig 中,將 bean 注入的方式有兩類:

  • ??一種是在 bean 中內(nèi)置 dig.In 標(biāo)識,執(zhí)行一次 Invoke 方法會自動完成 bean 的注入工作

  • ??另一種是通過 Container.Provide 方法,傳入 bean 的構(gòu)造器函數(shù).

Container.Provide 是主鏈路,接下里沿著該方法進(jìn)行源碼走讀.

(1)Container.Provide

經(jīng)由 Container.Provide -> Scope.Provide 的鏈路調(diào)用后,完成了對構(gòu)造器函數(shù)的類型和配置的檢查,隨后步入 Scope.provide 方法中.

?

?

(2)Scope.provide

Scope.provide 方法中完成的工作是:

  • ??調(diào)用 newConstructorNode 方法,將構(gòu)造器函數(shù)封裝成一個 node 節(jié)點

  • ??調(diào)用 Scope.findAndValidateResults 方法,通過解析構(gòu)造器出參的類型以及用戶定制的 bean 名稱/組名,封裝出對應(yīng)于出參個數(shù)的 key

  • ??將一系列 key-node 對添加到 Scope.providers map 當(dāng)中,供后續(xù)的 invoke 流程使用

  • ??將新生成的 node 添加到 Scope.nodes 數(shù)組當(dāng)中

?

(3)newConstructorNode

newConstructorNode 方法完成了將構(gòu)造器函數(shù) ctor 封裝成節(jié)點的任務(wù),其中包含幾個核心步驟:

  • ??調(diào)用 newParamList 方法,將入?yún)⒎庋b成 param 列表的形式,但還沒有真正從 container 中獲取 bean 執(zhí)行 param 的填充動作

  • ??調(diào)用 newResultList 方法,將出參封裝成 result 列表的形式,同樣只做封裝,沒有執(zhí)行將 result 注入容器的處理

  • ??結(jié)合構(gòu)造器函數(shù) ctor、入?yún)⒘斜?param list 和出參列表 result list,構(gòu)造 constructorNode 并返回

?

(4)newParamList

newParamList 方法中,會根據(jù) reflect 包的能力,獲取到構(gòu)造器函數(shù)的入?yún)⑿畔ⅲ⑵湔{(diào)用 newParam 方法將每個入?yún)⒎庋b成 param 的形式.

?

在 newParam 方法中,會根據(jù)入?yún)⒌念愋停捎貌煌臉?gòu)造方法,包括 paramSingle 和 paramObject 的類型.

?

(5)newResultList

newParamList 方法中,會根據(jù) reflect 包的能力,獲取到構(gòu)造器函數(shù)的出參信息,并將其調(diào)用 newReject 方法將每個出參封裝成 result 的形式.

?

在 newResult 方法中,會根據(jù)出參的類型,采用不同的構(gòu)造方法,包括 resultSingle 和 resultObject、resultGroup 的類型.

?

3.4 提取 bean

圖片

?

從容器中提取 bean 的入口是 Container.Invoke 方法,需要將 bean 提取器函數(shù)作為 Invoke 的第一個入?yún)ⅲ⑻崛∑骱瘮?shù)的入?yún)⒙暶鞒?bean 對應(yīng)的類型.

在 dig 提取 bean 的鏈路中,正是根據(jù)提取器函數(shù)的入?yún)㈩愋妥鞣瓷?,從容器中提取出對?yīng)的 bean.

?

(1)Container.Invoke

在 Container.Invoke-> Scope.Invoke 的鏈路中:

  • ??針對提取器函數(shù) function 和配置項 opts 進(jìn)行了校驗

  • ??通過 shallowCheckDependencies 方法進(jìn)行了依賴路徑的梳理,保證容器中已有的組件足以支撐構(gòu)造出本次 Invoke 需要獲得的 bean

  • ??調(diào)用 newParamList 方法,通過提取器函數(shù)的入?yún)?,?gòu)造出所需的 params 列表

  • ??調(diào)用 paramList.BuildList 方法,真正地從容器中提取到對應(yīng)的 bean 集合,通過 args 承載

  • ??調(diào)用 Scope.invokerFn 方法,傳入提取器函數(shù) function 和對應(yīng)的入?yún)?args,通過反射機(jī)制真正地執(zhí)行提取器函數(shù) function,在執(zhí)行過程中,入?yún)?args 就已經(jīng)是從容器中獲取到的 bean 了

?

?

(2)param.Build

paramList.BuildList 方法,會遍歷 params 列表,對每個 param 依次執(zhí)行 param.Build 方法,從容器中獲取到 bean 填充到 args 數(shù)組中并返回.

?

以 param interface 的實現(xiàn)類 paramSingle 為例,paramSingle.Build 方法的執(zhí)行步驟包括:

  • ??倘若 bean 已經(jīng)構(gòu)造過了,則通過 container.getValue 方法直接從 container.values 中獲取緩存好的 bean 單例進(jìn)行復(fù)用

  • ??調(diào)用 container.getValueProviders 方法,獲取 bean 對應(yīng)的 constructorNode

  • ??調(diào)用 constructorNode.Call 方法,通過執(zhí)行 bean 的構(gòu)造器函數(shù)創(chuàng)建 bean 并將其注入到 container.values 緩存 map 中

  • ??再次調(diào)用 container.getValue 方法,從 container.values 緩存 map 中獲取 bean 并返回

?

(3)constructorNode.call

constructorNode.call 方法核心步驟包括:

  • ??通過 constructorNode.called 標(biāo)識,保證每個構(gòu)造器函數(shù)不被重復(fù)執(zhí)行

  • ??調(diào)用 shallowCheckDependencies 方法,檢查構(gòu)造器節(jié)點 constructorNode 入?yún)?yīng)的 paramList 的依賴路徑是否完成

  • ??調(diào)用 paramList.BuildList 方法,將構(gòu)造器節(jié)點依賴的入?yún)?args 構(gòu)造出來(此時會遞歸進(jìn)入 3.4小節(jié)第(2)部分,從容器中提取 bean 填充構(gòu)造器函數(shù)的入?yún)?)

  • ??調(diào)用 Scope.invoker 方法,將構(gòu)造器函數(shù) constructorNode.ctor 及其入?yún)?args 傳入,通過reflect 包的能力真正執(zhí)行構(gòu)造器函數(shù),完成 bean 的構(gòu)造

  • ??調(diào)用 resultList.ExactList 方法,將構(gòu)造生成的 bean 添加到 container.values 緩存 map 中

  • ??將 constructorNode.called 標(biāo)識標(biāo)記為 true,代表構(gòu)造器函數(shù)已經(jīng)執(zhí)行過了

?

(4)result.Extract

在 resultList.ExtractList 方法中,會遍歷傳入的 results,分別執(zhí)行 result.Extract 方法,依次將 bean 添加到 container.values 緩存 map 中.

?

同樣以 resultSingle 為例,方法核心邏輯是以 result 的名稱和類型組成唯一的 key,以 bean 為 value,將 key-value 對添加到 contaienr.values 緩存 map.

?

(5)Scope.invokerFn

Scope 的 invokerFn 是獲取 bean 的入口函數(shù),默認(rèn)使用 defaultInvoker 函數(shù).

?

defaultInvoker 函數(shù)的形參分別為構(gòu)造器函數(shù)及其依賴的入?yún)?,方法?nèi)部會依賴 reflect 庫的能力,執(zhí)行構(gòu)造器函數(shù),并將響應(yīng)結(jié)果返回.

?

?

4 總結(jié)

最后來盤點一下本期我們討論到的內(nèi)容:

  • ??介紹了引入 Golang IOC 框架 dig 的背景——面向?qū)ο缶幊?成員依賴注入的代碼風(fēng)格

  • ??介紹了 dig 的基本用法:(1)創(chuàng)建容器 dig.New;(2)注入 bean 方法:Container.Provide;(3)提取 bean 方法:Container.Invoke

  • ??基于源碼走讀的方式,串講了通過 dig 創(chuàng)建容器、注入 bean 構(gòu)造器和提取 bean 三條方法鏈路的底層實現(xiàn)細(xì)節(jié)


低配 Spring—Golang IOC 框架 dig 原理解析的評論 (共 條)

分享到微博請遵守國家法律
灯塔市| 大安市| 奉化市| 绥滨县| 高要市| 偏关县| 余江县| 绍兴市| 沅陵县| 泰宁县| 宜章县| 铅山县| 临高县| 大理市| 黄石市| 五指山市| 昌平区| 农安县| 霞浦县| 茌平县| 武平县| 蚌埠市| 柳河县| 娄烦县| 平泉县| 尼勒克县| 贵南县| 盐源县| 武宁县| 通州市| 姜堰市| 高邑县| 瑞丽市| 鹤山市| 郎溪县| 凉山| 岳池县| 龙海市| 乌鲁木齐市| 闸北区| 阿拉尔市|