依賴注入:在項目開發中,常見一個類的實例依賴另外一個類實例的狀況 如java
public class Man(){
prviate Car car;
public Man(){
car=new Car();
}
}
複製代碼
常見的依賴方式基於構造方法,set設置等緩存
依賴注入是控制反轉的一種表現形式,被依賴的對象(Car)再也不由 依賴對象(Man)主動建立 ,而是由第三方容器去建立,而後注入到依賴對象(man)的過程,下降代碼耦合性bash
在build.gradle中添加依賴:ide
dependencies {
...
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
}
複製代碼
藉助上面的場景進行應用測試
Car相關代碼 因爲Car須要被注入,在構造方法上增長 @Inject 註解gradle
public class Car {
private CarDipan carDipan;
@Inject
public Car() {
}
public void pao() {
System.out.println("跑起來");
}
}
複製代碼
Man相關代碼 在Car變量上聲明 @Inject 註解ui
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代碼以下this
@Component
interface ManComponent {
void inject(Man man);
}
複製代碼
測試代碼google
public class DaggerTest {
public static void main(String[] args) {
Man man = new Man();
man.kaiche();
}
}
複製代碼
有一些第三方庫,咱們沒法修改源碼,即沒法用@Inject去註解構造方法。這時候就須要@Module和@Provides註解spa
@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註解和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 別名註解 和@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 有時候咱們不想運行時就把類建立好,而是在須要的時候建立類,這是就能夠經過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須要和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註解