Dagger2解析-1

如果還沒(méi)入門(mén)的症革,可以先去搜搜基本用法筐咧,本系列主要偏向原理

Dagger版本 2.11

1.Dagger2

Dagger2是啥,Google告訴我們:

Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier versioncreated by Square and now maintained by Google.
Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions. More details can be found in this talk(slides) by +Gregory Kick.

Dagger是為Android和Java平臺(tái)提供的一個(gè)完全靜態(tài)的噪矛,在編譯時(shí)進(jìn)行依賴(lài)注入的框架量蕊,原來(lái)是由Square公司維護(hù)的然后現(xiàn)在把這堆東西扔給Google維護(hù)了。Dagger解決了基于反射帶來(lái)的開(kāi)發(fā)和性能上的問(wèn)題(因?yàn)镈agger并沒(méi)有用反射來(lái)做依賴(lài)注入, 靠的是注解生成代碼實(shí)現(xiàn)依賴(lài)注入)

ps1:本文不講使用方法
ps2:本篇實(shí)例使用的都是kotlin

2.簡(jiǎn)單用法

舉個(gè)例子,Target類(lèi)里有一個(gè)Member類(lèi)的成員變量,也就是Target依賴(lài)Member

class Member constructor()
class Target(val member: Member)

看上去并沒(méi)有什么問(wèn)題,但是這并不是好的寫(xiě)法.
如果Target類(lèi)不單單依賴(lài)Member,還依賴(lài)很多類(lèi)的話(huà),就得寫(xiě)很多創(chuàng)建實(shí)例的代碼,甚至如果以后Member類(lèi)需要修改代碼時(shí),那還得修改Target類(lèi)的代碼.如果Member類(lèi)被很多類(lèi)依賴(lài)的話(huà),修改起來(lái)就更加困難.

所以dagger2就是用來(lái)完成這個(gè)實(shí)例化并賦值b = new Member();的事情(也就是依賴(lài)注入)

dagger的注解有component\inject\module\scpoe

首先來(lái)最簡(jiǎn)單的寫(xiě)法艇挨,Member構(gòu)造方法沒(méi)有參數(shù)残炮,Target依賴(lài)Member的情況,也就是上面例子那樣的

給Member的構(gòu)造函數(shù)添加@Inject注解缩滨,告訴dagger此類(lèi)是可以實(shí)例化的

class Member @Inject constructor()

接著定義一個(gè)Component势就,也就是注入器(dagger就是根據(jù)這個(gè)接口生成實(shí)際的注入代碼)

@Component
interface TargetComponent {
  fun inject(target:Target)
}

然后是Target泉瞻,member標(biāo)記@Inject表示該變量可以被注入(從上面生成注入器的操作也能看出注入的代碼不在類(lèi)本身里面,所以member變量是不能標(biāo)記為final或者私有的)

class Target {
  @Inject
  lateinit var member:Member
}

這樣標(biāo)注的過(guò)程就完成了苞冯,編譯之后袖牙,就會(huì)生成DaggerTargetComponent(名字為Dagger+你定義的Component接口)的名稱(chēng)的類(lèi),在需要注入的時(shí)候調(diào)用

val target = Target()
DaggerTestComponent.builder().build().inject(target)

這樣target里面的member實(shí)例就被注入進(jìn)去了

3.dagger生成的代碼解析

下面我們來(lái)看dagger生成的代碼舅锄,找到build/generated/kapt/debug/xxx(你的包名)


如果是java就不是kapt了鞭达,但生成代碼的地方都是在generated里面的

可以看到生成了三個(gè)類(lèi),

  • DaggerTargetComponent:表示這層依賴(lài)關(guān)系的組件
  • Member_Factory:提供依賴(lài)實(shí)例的工廠
  • Target_MembersInjector:就是實(shí)際用于注入依賴(lài)的注入器

下面再來(lái)看這三個(gè)類(lèi)

3.1 DaggerTargetComponent

DaggerTargetComponent

可以看到這是一個(gè)Builder模式(這里看上去簡(jiǎn)單皇忿,但依賴(lài)多了就復(fù)雜了)
這里主要看inject方法畴蹭,用注入器調(diào)用injectMembers方法,傳入的參數(shù)是我們需要注入Member的Target實(shí)例
跟進(jìn)injectMembers(target)方法


這就是一個(gè)dagger定義的接口鳍烁,那么就得看具體實(shí)現(xiàn)了叨襟,回到DaggerTargetComponent, 找到initialize方法,這里是注入器targetMembersInjector初始化的地方幔荒。
this.targetMembersInjector = Target_MembersInjector.create(Member_Factory.create());
先跟進(jìn)Member_Factory.create()方法芹啥,來(lái)到代碼生成的第二個(gè)類(lèi)Member_Factory

3.2 Member_Factory

Member_Factory

這就是一個(gè)簡(jiǎn)單的工廠類(lèi),用來(lái)提供Member實(shí)例的铺峭,我們知道Member的構(gòu)造方法被標(biāo)記@Inject了墓怀,加上又沒(méi)有參數(shù),所以生成的代碼就直接new了
其他沒(méi)啥好講了卫键,回到前面的
this.targetMembersInjector = Target_MembersInjector.create(Member_Factory.create());
接下來(lái)就是跟進(jìn)Target_MembersInjector.create()方法了

3.3 Target_MembersInjector

直接注入依賴(lài)的地方

這里主要就是injectMembers的實(shí)現(xiàn)了
instance.member = memberProvider.get();
memberProvider就是前面的工廠(Member_Factory實(shí)現(xiàn)了Factory<Member>傀履,而Factory<Member>繼承了Provider<Member>),提供了Member的實(shí)例
這里就能看出為啥Target的成員member不能是final(賦值在類(lèi)外)和至少得是包級(jí)的權(quán)限(同包不同類(lèi))

ps:關(guān)于生成代碼的包位置莉炉,基本上就是對(duì)應(yīng)的類(lèi)生成的包位置相同

  • TargetComponent->DaggerTargetComponent
  • Target->Target_MembersInjector
  • Member->Member_Factory
挪一下位置,生成的位置就不同了

4.依賴(lài)不能直接創(chuàng)建的情況

前面的例子中,Member的構(gòu)造函數(shù)是無(wú)參的钓账,那么如果有參數(shù)呢?
這里又分為兩種情況:

  • 參數(shù)對(duì)象無(wú)法直接創(chuàng)建
  • 參數(shù)對(duì)象可以直接創(chuàng)建

4.1參數(shù)對(duì)象無(wú)法直接創(chuàng)建

這里就是另外一個(gè)注解Module以及Provide的作用了絮宁,顧名思義梆暮,這Module就是Target所依賴(lài)的一個(gè)模塊提供者.

改寫(xiě)一下Member,構(gòu)造函數(shù)需要一個(gè)String類(lèi)型的參數(shù)
class Member @Inject constructor(val name: String)

然后創(chuàng)建一個(gè)MemberModule類(lèi)

@Module
class MemberModule {
    @Provides
    fun provideMember(): Member = Member("from module provider")
}

Module表示這是模塊绍昂,可以放在組件中啦粹。
Provides表示這是實(shí)例提供者。
這樣Dagger注入時(shí)就可以在模塊中尋找依賴(lài)對(duì)象的實(shí)例化方法

接著是Component

@Component(modules = arrayOf(MemberModule::class))
interface TargetComponent {
    fun inject(target: Target)
}

Component多了一個(gè)modules參數(shù)窘游,代表這個(gè)組件所包含的模塊唠椭,一個(gè)組件可以包含多個(gè)模塊,所以是參數(shù)是Module數(shù)組

這樣就改造完成了忍饰,編譯后贪嫂,注入的代碼和原來(lái)一樣

DaggerTargetComponent.builder()
            .build()
            .inject(target)

看看生成的代碼


為了方便看,又把幾個(gè)類(lèi)放在一個(gè)包里了

可以看到艾蓝,比之前多了一個(gè)MemberModule_ProvideMemberFactory

看回DaggerTargetComponent,這里比之前就多了一些代碼


3處變化
  1. builder多了一個(gè)參數(shù)力崇,就是MemberModule
  2. 這個(gè)MemberModule可以不傳斗塘,因?yàn)镸emberModule是構(gòu)造函數(shù)是無(wú)參的,能直接new
  3. 工廠不一樣了亮靴,create的傳參也不一樣了

接下來(lái)看看這個(gè)工廠


這個(gè)工廠在其他情況下才會(huì)用到逛拱,本例里是沒(méi)用的

對(duì)比一下可以發(fā)現(xiàn),其實(shí)就是再把Member的依賴(lài)name外包出去給別的提供者提供實(shí)例台猴,這種情況后面再講

新的工廠

代碼很簡(jiǎn)單朽合,從原來(lái)直接new變成了MemberModule的provideMember()方法產(chǎn)生實(shí)例而已

4.1.1Module不直接提供Member實(shí)例

上文提到一個(gè)沒(méi)用的工廠,那么什么時(shí)候會(huì)產(chǎn)生作用呢饱狂?
改造一下Module曹步,這次Provide不直接提供Member實(shí)例,返回一個(gè)String即可(Member的構(gòu)成函數(shù)就一個(gè)String類(lèi)型的參數(shù))

@Module
class MemberModule {

    @Provides
    fun provideMemberName(): String = "from module String provider"

//    @Provides
//    fun provideMember(): Member = Member("from module Module provider")
}

編譯休讳,看生成的代碼

工廠名字不一樣了

可以看到這個(gè)工廠是提供String類(lèi)型的

再來(lái)看看DaggerTargetComponent
上文沒(méi)用的工廠類(lèi)用上了

熟悉的配方讲婚,熟悉的味道

還是一樣的直接new,只不過(guò)參數(shù)name的實(shí)例化由MemberModule_ProvideMemberNameFactory提供

4.2參數(shù)對(duì)象可以直接創(chuàng)建

好俊柔,之前的Member的構(gòu)造參數(shù)是一個(gè)String筹麸,這個(gè)Dagger可沒(méi)法創(chuàng)建,那么如果是一個(gè)可創(chuàng)建的呢雏婶?例如Member又依賴(lài)一個(gè)MemberOfMember

class MemberOfMember @Inject constructor() 
class Member @Inject constructor(val memberOfMember: MemberOfMember)

把Component變回最初的樣子

@Component
interface TargetComponent {
    fun inject(target: Target)
}

老樣子物赶,看生成的代碼


大同小異,多了個(gè)MemberOfMember_Factory

provider的類(lèi)型從String變成了MemberOfMember而已

和最開(kāi)始Member無(wú)參構(gòu)造函數(shù)時(shí)生成的Member_Factory一毛一樣

再看看DaggerTargetComponent


嵌套工廠

看到這是不是有些明白了留晚?這里總結(jié)一下依賴(lài)的實(shí)例化

  1. 看能不能夠直接創(chuàng)建(帶@Inject的空構(gòu)造函數(shù))酵紫,能就直接new
  2. 不能直接創(chuàng)建的,看module中有沒(méi)有提供直接實(shí)例化的方法
  3. 沒(méi)有提供直接實(shí)例化方法的错维,看有沒(méi)有能夠滿(mǎn)足依賴(lài)實(shí)例化所需所有參數(shù)的實(shí)例化方法(這里會(huì)是一個(gè)遞歸的過(guò)程奖地,參數(shù)的實(shí)例化會(huì)重復(fù)123的步驟)

ps:所有可以被自動(dòng)實(shí)例化(1,3的情況)的對(duì)象構(gòu)造參數(shù)都必須帶@Inject
ps:2 @Inject聲明屬性表示這個(gè)屬性可以被注入赋焕,屬性本身不能是final参歹,也不能在包級(jí)別以下的權(quán)限
@Inject不是萬(wàn)能的,例如接口沒(méi)法實(shí)例化,或者是第三方的代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末隆判,一起剝皮案震驚了整個(gè)濱河市犬庇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜜氨,老刑警劉巖械筛,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異飒炎,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)笆豁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)郎汪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赤赊,“玉大人,你說(shuō)我怎么就攤上這事煞赢∨准疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵照筑,是天一觀的道長(zhǎng)竹握。 經(jīng)常有香客問(wèn)我抑进,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任诗祸,我火速辦了婚禮,結(jié)果婚禮上粪躬,老公的妹妹穿的比我還像新娘瑰排。我一直安慰自己,他們只是感情好支鸡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布冬念。 她就那樣靜靜地躺著,像睡著了一般牧挣。 火紅的嫁衣襯著肌膚如雪急前。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天瀑构,我揣著相機(jī)與錄音叔汁,去河邊找鬼。 笑死检碗,一個(gè)胖子當(dāng)著我的面吹牛据块,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播折剃,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼另假,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了怕犁?” 一聲冷哼從身側(cè)響起边篮,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奏甫,沒(méi)想到半個(gè)月后戈轿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阵子,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年思杯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挠进。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡色乾,死狀恐怖誊册,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情暖璧,我是刑警寧澤案怯,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站澎办,受9級(jí)特大地震影響嘲碱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜局蚀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一麦锯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧至会,春花似錦离咐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至县貌,卻和暖如春术陶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煤痕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工梧宫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摆碉。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓塘匣,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親巷帝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忌卤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容