依賴注入
依賴注入:在專案開發中,常見一個類的例項依賴另一個類例項的情況 如
public class Man(){
prviate Car car;
public Man(){
car=new Car();
}
}
複製程式碼
常見的依賴方式基於構造方法,set設定等
依賴注入是控制反轉的一種表現形式,被依賴的物件(Car)不再由 依賴物件(Man)主動建立 ,而是由第三方容器去建立,然後注入到依賴物件(man)的過程,降低程式碼耦合性
整合與使用
在build.gradle中新增依賴:
dependencies {
...
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
}
複製程式碼
藉助上面的場景進行應用
Car相關程式碼 由於Car需要被注入,在構造方法上增加 @Inject 註解
public class Car {
private CarDipan carDipan;
@Inject
public Car() {
}
public void pao() {
System.out.println("跑起來");
}
}
複製程式碼
Man相關程式碼 在Car變數上宣告 @Inject 註解
public class Man {
@Inject
Car car;
public Man() {
//該行程式碼後面講到
DaggerManComponent.create().inject(this);
}
public void kaiche() {
car.pao();
}
}
複製程式碼
此時依賴和被依賴的類都建立好了,還缺少一個紐帶把他們連線起來,紐帶就是 @Component
@Component 註解需要作用到介面或者抽象類上,新增抽象方法,引數是需要注入依賴的類,會自動生成以Dagger開頭的實現類
在Man的構造方法中進行注入 DaggerManComponent.create().inject(this); (可能會報找不到該類,需要rebuild下)
ManComponent程式碼如下
@Component
interface ManComponent {
void inject(Man man);
}
複製程式碼
測試程式碼
public class DaggerTest {
public static void main(String[] args) {
Man man = new Man();
man.kaiche();
}
}
複製程式碼
Module和Provides註解
有一些第三方庫,我們無法修改原始碼,即無法用@Inject去註解構造方法。這時候就需要@Module和@Provides註解
- @Module 註解到類上,表示這個類可以作為一個模組去引入到被@Component註解的介面上,規定用@Module註解的類名以Module做字尾
- @Provides用於被@Module標註類的方法中,規定用provide作為字首
假設Man物件包含一個FangZi物件,該物件構造方法無法用@Inject註解標註,那麼可以提供以下類
@Module
public class FangModule {
@Provides
public Fang provideFang() {
return new Fang();
}
}
複製程式碼
對應的ManComponent類也需要修改
@Component(modules = FangModule.class)
interface ManComponent {
void inject(Man man);
}
複製程式碼
這樣Man裡面的房子屬性就會成功注入。被注入的屬性一定要用@Inject註解
Module類的構造方法可以帶有引數,需要注意的是當Module類有帶有引數的構造方法時,
DaggerManComponent不會有Create方法,需要通過DaggerManComponent.builder().fangModule(new FangModule(引數)).build()手動傳入module物件
Bind註解
Bind註解和Provides注意類似 ,也是放到被Module類的方法上,不同的是Bind註解的方法是抽象方法,引數型別是返回值型別的子類。 用於注入某個介面或者抽象類時,可以獲取他的實現子類。或者可以說將一種型別轉換成他的父類或介面,只暴露父類或介面的方法
BindModule.java
@Module
public abstract class BindModule {
@Binds
public abstract Manger bindManager(ManagerImpl manager);
public static abstract class Manger {
public abstract String getName();
}
public static class ManagerImpl extends Manger {
@Inject
public ManagerImpl() {
}
@Override public String getName() {
return "我是ManagerImpl";
}
}
}
Man.java
public class Man {
@Inject
BindModule.Manger manger;//這是獲取的時他的子類MangerImpl
public Man() {
DaggerManComponent.create().inject(this);
}
public void run() {
System.out.println(manger.getName());
}
}
複製程式碼
Named、Singleton和Reusable
@Named 別名註解 和@Provides配合使用 比如我有兩套房子,但是返回值都是房子 如果不用@Named註解,dagger不知道我們們要哪個房子,就會迷失,這時就需要起一個別名
@Module
public class FangModule {
@Named("北三環")
@Provides
public Fang provideFang() {
return new Fang("北三環");
}
@Named("西四環")
@Provides
public Fang provideFang2() {
return new Fang("西四環");
}
}
複製程式碼
在獲取房子是就必須註明@Named 表示 要哪一個
public class Man {
@Inject
Car car;
@Named("北三環")
@Inject
Fang fang1;
@Named("西四環")
@Inject
Fang fang2;
}
複製程式碼
@Qualifier 元註解 註解在@Named上 我們可以用 Qualifier自定義和 @Named功能相同的 註解
@Singleton 單例註解 在一個Component元件中單例 Singleton標註在Component上 和 Provides標註的方法或者標註在要注入的類上。
實現機制 是通過DoubleCheck來保證單例,在生成的DaggerManComponent類的初始化中,會把Provider轉換為DoubleCheck
在DoubleCheck的get方法中保證例項唯一
@Module
public class FangModule {
@Singleton
@Provides
public Fang provideFang() {
return new Fang("北三環");
}
}
複製程式碼
@Singleton
@Component(modules = FangModule.class)
interface ManComponent {
void inject(Man man);
}
複製程式碼
@Scope 元註解 註解在@Singleton 我們可以用 @Scope自定義和 @Singleton功能相同的 註解
@Reusable 註解 有時我們為了限制物件的建立次數,使用時可以從快取中獲取物件,可以使用該註解 該註解標註在 Provides標註的方法或者標註在要注入的類上。
該機制會把Provider轉換為SingleCheck來 使用SingleCheck來保證如果無物件會建立物件 如果有則會從快取中獲取
@Module
public class FangModule {
@Reusable
@Provides
public Fang provideFang() {
return new Fang("北三環");
}
}
複製程式碼
@Component(modules = FangModule.class)
interface ManComponent {
void inject(Man man);
}
複製程式碼
Lazy和Provider
Lazy 有時候我們不想執行時就把類建立好,而是在需要的時候建立類,這是就可以通過Lazy包裝要被注入的成員 ,使用時通過get方式獲取
public class Man {
@Inject
Lazy<Car> car;
public Man() {
DaggerManComponent.create().inject(this);
}
public void kaiche() {
car.get().pao();
}
}
複製程式碼
Provider 有時我們 需要建立類的多個例項,可以通過Provider包裝要被注入的成員,每次執行get則會建立一個新的例項
IntoSet和IntoMap註解
IntoSet需要和Provides或Bind一起使用,我們知道在Module中如果有兩個方法返回值會導致依賴迷失,需要用Named註解起別名。 但是如果這些方法被@IntoSet註解修飾,相同的返回值產生的物件會被放到同一個Set集合中。同時注入該類物件時,需要用Set去包裝
FangModule.java
@Module
public class FangModule {
@IntoSet
@Provides
public Fang provideFang1() {
return new Fang("國貿");
}
@IntoSet
@Provides
public Fang provideFang2() {
return new Fang("望京");
}
}
Man.java
public class Man {
@Inject
Set<Fang> fangs;
public Man() {
DaggerManComponent.builder().fangModule(new FangModule()).build().inject(this);
}
public void run() {
fangs.forEach(Fang::get);
}
}
複製程式碼
IntoMap和IntoSet類似,不過是將發返回的物件放入到map集合中。用IntoMap註解時, 必須指明Key.使用 @IntKey/ @LongKey/@StringKey註解 也可以使用@MapKey元註解,自定義Key註解