Dagger
為Android和Java打造的依賴注入框架
簡介
略
使用Dagger
我們將使用一個咖啡機的例子來展示依賴注入和Dagger。
聲明依賴
Dagger創(chuàng)建一個你的應(yīng)用的實例并且滿足他們的依賴。它使用javax.inject.Inject 注解來標(biāo)明哪個構(gòu)造函數(shù)或者成員屬性是它感興趣的。
使用@Inject來注解一個Dagger用來創(chuàng)建一個實例的構(gòu)造方法。當(dāng)一個新的實例被申請的時候,Dagger就會獲取到請求參數(shù)值并且調(diào)用構(gòu)造方法。
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
...
}
Dagger也可以直接注入屬性。下面這個例子直接為pump屬性和heater屬性分別獲取了一個實例。
class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
...
}
如果你的類只有被 @Inject 標(biāo)注的屬性,但是卻沒有被其標(biāo)注的構(gòu)造方法,Dagger將使用無參構(gòu)造方法(如果它存在的話)。缺少@Inject標(biāo)注的類不能被Dagger構(gòu)建。
Dagger不支持方法注入。
滿足依賴
一般來講,Dagger會使用上述方式來構(gòu)造一個對應(yīng)請求類型的實例,以此來滿足依賴。當(dāng)你請求了一個CofferMaker,將會調(diào)用new CofferMaker()并把它關(guān)聯(lián)到屬性中。
但是 @Inject 不能在以下情況工作:
- 接口不能被構(gòu)造
- 第三方類不能被構(gòu)造
- 要注冊的對象必須已經(jīng)標(biāo)注過!
在這種情況下尷尬的情況下,可以使用 @Provides 注解一個方法來滿足依賴。方法的返回值定義了它將滿足的依賴。
舉個例子,provideHeater()將在Heater被請求時調(diào)用:
@Provides Heater provideHeater() {
return new ElectricHeater();
}
被@Provides標(biāo)注的方法也有可能有他們自己的依賴。當(dāng)請求一個Pump的時候,這個方法返回一個 Thermosiphon 。
@Provides Pump providePump(Thermosiphon pump) {
return pump;
}
所有的 @Provides的方法屬于module。這是一個被@Module標(biāo)注的類:
@Module
class DripCoffeeModule {
@Provides Heater provideHeater() {
return new ElectricHeater();
}
@Provides Pump providePump(Thermosiphon pump) {
return pump;
}
}
依照慣例,@Provides方法一般會有一個provide前綴,Module類一般會有一個Module后綴。
創(chuàng)建圖
被@Inject 和 @Provides標(biāo)注的類之間的依賴關(guān)系,可以使用一個對象圖來表示。可以使用ObjectGraph.create()來獲取這個圖,其可以接收一個或者多個module。
ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
為了這個圖能使用,我們需要去引導(dǎo)注入(bootstrap injection)。一般會在命令行程序中的main類中,或者在一個Android App的Activity中請求注入。在我們咖啡機的示例程序中,CoffeeApp類被用來啟動依賴注入。我們讓圖來提供一個注入的示例:
class CoffeeApp implements Runnable {
@Inject CoffeeMaker coffeeMaker;
@Override public void run() {
coffeeMaker.brew();
}
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class);
...
}
}
只剩一件事還沒做了,那就是圖還不知道CoffeeApp類。我們需要像注冊一個module一樣顯示地注冊它:
@Module(
injects = CoffeeApp.class
)
class DripCoffeeModule {
...
}
注入操作將會在編譯期就讓圖生效。以此來發(fā)現(xiàn)一些問題。(Detecting problems early speeds up development and takes some of the danger out of refactoring.)
現(xiàn)在圖已經(jīng)被建立起來,而且根對象也被注入了,我們來運行一下我們的咖啡機。好玩吧。
$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P