Dagger2 依賴的接力游戲(三):Module的使用和原理

文接Dagger2 依賴的接力游戲(二)

PS: 本篇文章的示例代碼,放在了項目的chapter3分支

上一篇文章我們介紹了使用@inject聲明提供類型,并據(jù)此實(shí)現(xiàn)了Car和Engine的完全依賴注入,這篇文章我們使用Module來實(shí)現(xiàn)同樣的效果。

我們先梳理一下上篇文章里的依賴關(guān)系,我們最后總結(jié)了它的依賴模型是這樣的:

D(Car,Engine) = D(Car,DaggerCarComponent)

加上Dagger2托管的依賴關(guān)系實(shí)際上是這樣的:

D(Car,Engine) = D(Car,DaggerCarComponent) + D(DaggerCarComponent,Car構(gòu)造方法) + D(Car構(gòu)造方法, Engine構(gòu)造方法)

我們先調(diào)整Engine的提供,使用Module來實(shí)現(xiàn)。

定義EngineModule

首先我們聊一下什么是Module,Module是Dagger2為我們定義的一個角色,它是一個“提供類型P(?)”的容器,我們用@Module注解一個類來聲明這個類是一個module,在這個類里面,所有用@Provides注解的返回類型不為空的方法都是合法的提供類型。要注意的是,Dagger2框架不為Null對象做任何的錯誤兼容處理,相反地,它會在注入前檢查Module創(chuàng)建的對象是否為空,如果為空就報空指針錯誤,所以我們要確保Module的提供方法返回一個可用的對象。

如果我們想返回可能為空的對象,可以使用@Nullable注解聲明,這個注解是類型遞歸的,使用了這個注解的方法,Component里也必須同樣聲明,否則會編譯失敗。

現(xiàn)在我們創(chuàng)建一個EngineModule來實(shí)現(xiàn)P(Engine)。

@Module
public class EngineModule {

    @Provides
    Engine provideEngine(){
        return new Engine(2);
    }

}

聲明對EngineModule的依賴

創(chuàng)建后我們要告訴CarComponent使用EngineModule來作為它的提供類型的容器,查找它需要的提供類型。Component注解定義了modules索引的class數(shù)組參數(shù),用來聲明它依賴的所有Module。

這里和使用Inject注解構(gòu)造方法不同之處在于,類的構(gòu)造方法總是唯一的(參數(shù)類型唯一),因此它作為“提供類型”也是唯一的,所以Dagger2會自動將構(gòu)造方法聲明的提供類型,作為所有Component的備用類型,不用我們手動告訴具體的Component。但是如果在Module里聲明提供類型,我們可以在多個Module里聲明相同的提供類型,根據(jù)不同的使用需求對Component進(jìn)行裝配,因此多了告訴Component它的Modules依賴這一步,這一步也是我們使用Dagger2來管理依賴關(guān)系的優(yōu)越性的體現(xiàn):我們只要修改注解,就可以修改具體的依賴關(guān)系。

我們修改一下CarComponent接口:

@Component(modules = EngineModule.class)
public interface CarComponent {

    void inject(Car car);

    Car getCar();
}

現(xiàn)在我們使用Module聲明了Engine的提供類型,main方法的調(diào)用方式保持不變:

public class Main {
    
    public static void main(String[] args){
        Car car = DaggerCarComponent.builder().build().getCar();
        System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());
    }
}

我們運(yùn)行一下:

cylinderNumbers : 2

Process finished with exit code 0

Engine的氣缸數(shù)目已經(jīng)變?yōu)榱?,也就是說我們修改生效了。

Module的工作機(jī)制

現(xiàn)在我們再看看生成的注入代碼,了解一下Module的工作機(jī)制。我們先看下getCar方法:

  @Override
  public Car getCar() {
    return injectCar(
        Car_Factory.newCar(EngineModule_ProvideEngineFactory.proxyProvideEngine(engineModule)));
  }

對比使用@Inject聲明的模板代碼,我們發(fā)現(xiàn)前半部分

 injectCar(Car_Factory.newCar(xxx));

是一樣的,不同的是前者直接創(chuàng)建了一個Engine對象,而這里使用了模板來獲取對象,Dagger為我們生成了與Module名稱和方法名稱對應(yīng)關(guān)系為:

模塊名_方法名Factory.proxy方法名(模塊名 instance)

的靜態(tài)類和方法,接受EngineModule對象,并作為它的代理來獲取對象,我們看看代理方法:

  public static Engine proxyProvideEngine(EngineModule instance) {
    return Preconditions.checkNotNull(
        instance.provideEngine(), "Cannot return null from a non-@Nullable @Provides method");
  }

這里調(diào)用了Module實(shí)例中我們自己定義的provideEngine方法,并校驗(yàn)module生成的對象是否為空。我們再看看這個EngineModule實(shí)例是怎么初始化的:

    public CarComponent build() {
      if (engineModule == null) {
        this.engineModule = new EngineModule();
      }
      return new DaggerCarComponent(this);
    }

在使用Builder構(gòu)建CarComponent對象的時候,如果為空就創(chuàng)建一個新的實(shí)例,并傳遞給Component對象。同時我們也看到,在Builder中生成了指定module的方法:

    public Builder engineModule(EngineModule engineModule) {
      this.engineModule = Preconditions.checkNotNull(engineModule);
      return this;
    }

所以我們可以總結(jié)一下,如果我們?yōu)锳Component實(shí)現(xiàn)XModule的YMethod方法,來聲明一個P<Type>,Dagger為我們做了以下的工作來完成這個功能。

  1. 生成XModule_YMethodFactory.proxyYMethod(XModule instance)的靜態(tài)代理類和方法,并作為Type實(shí)例的獲取代理
  2. 在AComponent和AComponent.Builder中生成XModule的成員,并為XModule成員生成建造模式的初始化方法。
  3. 在getType()方法里調(diào)用第1步生成的代理方法,獲取Type實(shí)例作為依賴,創(chuàng)建我們要的目標(biāo)實(shí)例。

這個環(huán)節(jié)完成的依賴模型替換為:

D(DaggerCarComponent,Engine) = D(DaggerCarComponent,EngineModule_ProvideEngineFactory) + D(EngineModule_ProvideEngineFactory, EngineModule) + D(EngineModule,Engine)

這里值得注意的有以下幾點(diǎn):

  1. 之所以在Builder里生成XModule成員的建造模式方法,是為了提供機(jī)制和能力,讓我們更加靈活地管理具體的Module,控制對象實(shí)例化邏輯。
  2. D(EngineModule,Engine) 是我們手動實(shí)現(xiàn)的,是我們真正的業(yè)務(wù)邏輯,需要我們多加關(guān)注,保證邏輯符合預(yù)期。

小結(jié)

在這一節(jié)中,我們使用module來實(shí)現(xiàn)了Engine的提供,并通過源碼和邏輯模型,分析了module的工作機(jī)制以及其背后的設(shè)計原理。但是我們看到,作為Car類的組件,依賴了Engine的Module,稍微有點(diǎn)別扭。有時候我們想控制Component、Module的對應(yīng)關(guān)系,比如為了使我們的代碼邏輯更加清晰可讀,將這種復(fù)用的需求,通過組件的復(fù)用來實(shí)現(xiàn)。下一篇文章,我們將講解如何復(fù)用Component,敬請期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容