Dagger2使用

為什么使用Dagger2

無論是構造函數注入還是接口注入,都避免不了要編寫大量的模板代碼。機智的猿猿們當然不開心做這些重復性的工作,于是各種依賴注入框架應用而生。但是這么多的依賴注入框架為什么我們卻偏愛Dagger2呢?我們先從Spring中的控制反轉(IOC)說起。

談起依賴注入,做過J2EE開發的同學一定會想起Spring IOC,那通過迷之XML來配置依賴的方式真的很讓人討厭;而且XML與Java代碼分離也導致代碼鏈難以追蹤。之后更加先進的Guice(Android端也有個RoboGuice)出現了,我們不再需要通過XML來配置依賴,但其運行時實現注入的方式讓我們在追蹤和定位錯誤的時候卻又萬分痛苦。開篇提到過Dagger就是受Guice的啟發而開發出來的;Dagger繼承了前輩的思想,在性能又碾壓了它的前輩Guice,可謂是長江后浪推前浪,前浪死在沙灘上。

又如開篇我在簡介中說到的,Dagger是一種半靜態半運行時的DI框架,雖說依賴注入是完全靜態的,但是生成有向無環圖(DAG)還是基于反射來實現,這無論在大型的服務端應用還是在Android應用上都不是最優方案。升級版的Dagger2解決了這一問題,從半靜態變為完全靜態,從Map式的API變成申明式API(@Module),生成的代碼更優雅高效;而且一旦出錯我們在編譯期間就能發現。所以Dagger2對開發者的更加友好了,當然Dagger2也因此喪失了一些靈活性,但總體來說利還是遠遠大于弊的。

前面提到這種A B C D E連續依賴的問題,一旦E的創建方式發生了改變就會引發連鎖反應,可能會導致A B C D都需要做針對性的修改;但是騷年,你以為為這僅僅是工作量的問題嗎?更可怕的是我們創建A時需要按順序先創建E D C B四個對象,而且必須保證順序上是正確的。Dagger2就很好的解決了這一問題(不只是Dagger2,在其他DI框架中開發者同樣不需要關注這些問題)。

Dagger2注解

開篇我們就提到Dagger2是基于Java注解來實現依賴注入的,那么在正式使用之前我們需要先了解下Dagger2中的注解。Dagger2使用過程中我們通常接觸到的注解主要包括:@Inject, @Module, @Provides, @Component, @Qulifier, @Scope, @Singleten。

@Inject:@Inject有兩個作用,一是用來標記需要依賴的變量,以此告訴Dagger2為它提供依賴;二是用來標記構造函數,Dagger2通過@Inject注解可以在需要這個類實例的時候來找到這個構造函數并把相關實例構造出來,以此來為被@Inject標記了的變量提供依賴;

@Module:@Module用于標注提供依賴的類。你可能會有點困惑,上面不是提到用@Inject標記構造函數就可以提供依賴了么,為什么還需要@Module?很多時候我們需要提供依賴的構造函數是第三方庫的,我們沒法給它加上@Inject注解,又比如說提供以來的構造函數是帶參數的,如果我們之所簡單的使用@Inject標記它,那么他的參數又怎么來呢?@Module正是幫我們解決這些問題的。

@Provides:@Provides用于標注Module所標注的類中的方法,該方法在需要提供依賴時被調用,從而把預先提供好的對象當做依賴給標注了@Inject的變量賦值;

@Component:@Component用于標注接口,是依賴需求方和依賴提供方之間的橋梁。被Component標注的接口在編譯時會生成該接口的實現類(如果@Component標注的接口為CarComponent,則編譯期生成的實現類為DaggerCarComponent),我們通過調用這個實現類的方法完成注入;

@Qulifier:@Qulifier用于自定義注解,也就是說@Qulifier就如同Java提供的幾種基本元注解一樣用來標記注解類。我們在使用@Module來標注提供依賴的方法時,方法名我們是可以隨便定義的(雖然我們定義方法名一般以provide開頭,但這并不是強制的,只是為了增加可讀性而已)。那么Dagger2怎么知道這個方法是為誰提供依賴呢?答案就是返回值的類型,Dagger2根據返回值的類型來決定為哪個被@Inject標記了的變量賦值。但是問題來了,一旦有多個一樣的返回類型Dagger2就懵逼了。@Qulifier的存在正式為了解決這個問題,我們使用@Qulifier來定義自己的注解,然后通過自定義的注解去標注提供依賴的方法和依賴需求方(也就是被@Inject標注的變量),這樣Dagger2就知道為誰提供依賴了。----一個更為精簡的定義:當類型不足以鑒別一個依賴的時候,我們就可以使用這個注解標示;

@Scope:@Scope同樣用于自定義注解,我能可以通過@Scope自定義的注解來限定注解作用域,實現局部的單例;

@Singleton:@Singleton其實就是一個通過@Scope定義的注解,我們一般通過它來實現全局單例。但實際上它并不能提前全局單例,是否能提供全局單例還要取決于對應的Component是否為一個全局對象。

我們提到@Inject和@Module都可以提供依賴,那如果我們即在構造函數上通過標記@Inject提供依賴,有通過@Module提供依賴Dagger2會如何選擇呢?具體規則如下:

步驟1:首先查找@Module標注的類中是否存在提供依賴的方法。

步驟2:若存在提供依賴的方法,查看該方法是否存在參數。

a:若存在參數,則按從步驟1開始依次初始化每個參數;

b:若不存在,則直接初始化該類實例,完成一次依賴注入。

步驟3:若不存在提供依賴的方法,則查找@Inject標注的構造函數,看構造函數是否存在參數。

a:若存在參數,則從步驟1開始依次初始化每一個參數

b:若不存在,則直接初始化該類實例,完成一次依賴注入。

Dagger2注解@Module ,@Component,@Inject的關系

簡單的說,就是一個工廠模式,由Dagger負責創建工廠,幫忙生產instance。遵從Java規范JSR 330,可以使用這些注解。現在不研究Dagger2是如何根據注解去生成工廠的,先來看看工廠是什么東西,理解為什么可以實現了DI(Dependency Injection),如何創建IoC(Inverse of Control)容器。

Dagger2是通過依賴注入完成類的初始化。

這個過程需要三部分:

#1****依賴提供方(生產者)

#2****依賴注入容器(橋梁)

#3****依賴需求方(消費者)

image

總結:

@Inject主要有兩個作用

#1作為依賴注提供方

使用@Inject注解構造方法。

注解構造函數,讓Dagger2幫我們實例化該,并注入。

#2作為依賴需求方:

使用@Inject注解成員。

如果一個成員變量被@Inject注解修飾,并且成員類構造函數也被@Inject注解,那么dagger2幫我們實例化該成員類,并注入。

通常在需要依賴的地方使用這個注解。換句話說,你用它告訴Dagger這個類或者字段需要依賴注入。這樣,Dagger就會構造一個這個類的實例并滿足他們的依賴。

使用@Inject可以讓IoC容器負責生成instance,如果沒有這個注解,dagger將不認識,當做普通類,無法代理

@Module的作用

#1@Module注解,負責管理依賴。

Module 其實是一個簡單工廠模式,Module 里面的方法都是創建相應類實例的方法。

#2通過@Module獲得第三方類庫的對象。

#3@Module是一個依賴提供方的合集。

@ModulepublicclassAModule{@ProvidespublicGsonprovideGson(){returnnewGson();}}

@Provides

#1注解@Module中的方法

在modules中,我們定義的方法是用這個注解,以此來告訴Dagger我們想要構造對象并提供這些依賴。

@Component的作用

#1@Component一般用來注解接口

#2負責在@Inject和@Module之間建立連接。

也可以說是@Inject和@Module的橋梁,它的主要作用就是連接這兩個部分。

#3實例化@Inject注解的時,遇到沒有構造函數的類依賴,則該依賴由@Module修飾的類提供。

#4****依賴注入容器只是一個接口interface。

Component需要引用到目標類的實例,Component會查找目標類中用Inject注解標注的屬性,查找到相應的屬性后會接著查找該屬性對應的用Inject標注的構造函數(這時候就發生聯系了),剩下的工作就是初始化該屬性的實例并把實例進行賦值。因此我們也可以給Component叫另外一個名字注入器(Injector)

Component注解的類,再編譯之后,會生產一個以Dagger+類名的一個類,如下面的MainComponent會生成類DaggerMainComponent(補充一點,Kotlinkapt編譯生成類的位置:\build\generated\source\kapt\debug),我們需要在目標類MainActivity中加入下面代碼

DaggerMainComponent.builder()

            .build()

            .inject(this)

DaggerMainComponent使用了建造者設計模式,inject方法是我們MainComponent中定義的,這樣目標類就和Component建立了聯系.Component會去遍歷使用@Inject注解的常量,然后去查找對應的類是否有@Inject注解的構造方法,如果沒有就會報異常.

@Component{modules={HeaterModule.class,PumperModule.class}}publicinterfaceMachineComponent{voidinject(CoffeeMachine machine);}

dagger中Component就是最頂級的入口,dagger為之生成了工廠類 DaggerMachineComponent,目標是構建CoffeeMachine, 在CoffeeMachine中使用了Injection,那么依賴要由工廠類來提供。工廠類是根據modules的參數來找依賴綁定的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372