天天看點

Dagger1依賴注入

文章目錄

    • 1.概述
    • 2.DECLARING DEPENDENCIES(聲明依賴)
    • 3.滿足依賴關系(SATISFYING DEPENDENCIES)
    • 4.建圖(BUILDING THE GRAPH)
    • 5.單例(SINGLETONS)
    • 6.懶加載(LAZY INJECTIONS)
    • 7.PROVIDER INJECTIONS
    • 8.限定符(QUALIFIERS)
    • 9.靜态注入(STATIC INJECTION)
    • 10.編譯時驗證 (COMPILE-TIME VALIDATION)
    • 11.編譯時代碼生成(COMPILE-TIME CODE GENERATION)
    • 12.子產品重寫(MODULE OVERRIDES)

dagger官方文檔

dagger1 git

1.概述

依賴注入(Dependency Injection)具體含義是:

當某個角色(一個Java執行個體,調用者)需要另一個角色(另一個Java執行個體,被調用者)的協助時,在傳統的程式設計過程中,通常由調用者來建立被調用者的執行個體。但在依賴注入器中,建立被調用者的工作不再由調用者來完成(控制反轉)

Dagger作為工廠類的替代品,聲明依賴、指定如何滿足依賴、主導應用程式。基于JSR-330,友善每個類測試。依賴注入不僅僅用于測試,還便于建立可重用、可替換的子產品。可在所有應用程式中共享相同的AuthenticationModule(驗證子產品),可在開發期間運作DevLoggingModule(開發者登入子產品),并在生産環境中運作ProdLoggingModule(使用者登入子產品),以在每種場景下獲得正确的行為

2.DECLARING DEPENDENCIES(聲明依賴)

使用 javax.inject.Inject 注解來識别構造函數和屬性:

(1)使用@Inject注解類的構造函數,當依賴注入建構執行個體時,Dagger會獲得所需的參數值并調用此構造函數

import javax.inject.Inject;

class Thermosiphon implements Pump {
  private final Heater heater;

  @Inject
  Thermosiphon(Heater heater) {
    this.heater = heater;
  }

  @Override public void pump() {
    if (heater.isHot()) {
      System.out.println("=> => pumping => =>");
    }
  }
}

           

(2)Dagger直接注入屬性:

import dagger.Lazy;
import javax.inject.Inject;

class CoffeeMaker {
  @Inject Lazy<Heater> heater; 
  @Inject Pump pump;

  public void brew() {
    heater.get().on();
    pump.pump();
    System.out.println(" [_]P coffee! [_]P ");
    heater.get().off();
  }
}
           

若類中有@Inject注解在屬性上,但沒有構造函數使用@Inject注解,Dagger會使用無參數構造函數(存在無參構造函數)。

Dagger不支援方法注入。

3.滿足依賴關系(SATISFYING DEPENDENCIES)

預設情況下,Dagger通過構造符合類型的執行個體來滿足每個依賴。

當請求注入一個CoffeeMaker時,通過調用new CoffeeMaker()并設定可注入的屬性。

@Inject并不适用于所有地方:

接口不能被構造;

第三方類不能被注解;

必須配置可配置對象。

對于不适用@Inject的情況,使用@ Provides注解方法來滿足依賴。方法的傳回類型定義了它所滿足的依賴項。

在任何需要Heater的時候,調用provideHeater()方法

Dagger1依賴注入

@Provides注解的方法可能有自己的依賴。當需要Pump 時,傳回Thermosiphon :

Dagger1依賴注入

所有@Provides注解的方法必須在一個子產品中,有@Module标準的類

Dagger1依賴注入

按照慣例,@Provides注解的方法方法名使用字首provide,而子產品類的類名使用字尾Module。

4.建圖(BUILDING THE GRAPH)

@Inject和@ Provides注釋的類形成了一張通過依賴連接配接的對象圖。通過調用ObjectGraph.create()獲得此圖,該函數接受一個或多個子產品作為參數:

Dagger1依賴注入

為了使用圖,需要引導注入。這通常需要注入指令行app的主類。在示例中,使用CoffeeApp類用于啟動依賴注入。要求圖提供一個類的注入執行個體:

import javax.inject.Inject;

import dagger.ObjectGraph;

public 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.run();
  }
}

           

唯一缺少的是可注入類CoffeeApp不為圖所知,需要顯式地在@Module注解中将它注冊為可注入類型。

Dagger1依賴注入

這些injects允許在編譯時驗證整張圖。盡早發現問題加快開發速度,并減少重構帶來的危險。

現在已經建構了圖并注入了根對象,運作coffee maker應用程式。

5.單例(SINGLETONS)

用@Singleton注解 帶@ Provides的方法或可注入類。對象圖将為所有使用者使用單例。

Dagger1依賴注入

注入類上的@Singleton注釋可作為說明文檔使用,提醒維護者人員,這可能由多線程共享。

6.懶加載(LAZY INJECTIONS)

有時需要在使用時執行個體化對象。對于任何綁定類型T,可建立 Lazy,延遲執行個體化直到第一次調用Lazy的get()方法。如果T單例,那麼Lazy為ObjectGraph範圍内所有注解注入的同一執行個體。否則,每個注入點将獲得其特有的Lazy執行個體。

class GridingCoffeeMaker {
  @Inject Lazy<Grinder> lazyGrinder;

  public void brew() {
    while (needsGrinding()) {
      // Grinder created once on first call to .get() and cached.
      lazyGrinder.get().grind();
    }
  }
}
           

7.PROVIDER INJECTIONS

有時需要傳回多個執行個體,而不是隻注入一個值。存在多種方法(工廠模式、構造模式),其中一個方法是注入一個Provider。Provider每次調用.get()方法時,建立一個新的T類型執行個體。

注意:注入Provider可能産生混亂的代碼,在對象圖中産生作用域錯誤或結構錯誤的對象。通常,使用Factory或Lazy或者重新組織代碼的生命周期和結構,以便注入T。但是,在某些情況下使用Provider.如,servlet在設計上是單例的,僅在請求特定資料的上下文中有效。

8.限定符(QUALIFIERS)

有時類型不足以識别依賴,需要添加了一個限定符注解。任何使用@Qualifier的注解均屬于限定符注解。例如@Named(javax.inject的内置限定符注解)

Dagger1依賴注入

可建立自己的限定符注釋,或者使用@Named注解。通過注解所需的屬性或參數來運用限定符。類型和限定符注釋都将用于識别依賴。

class ExpensiveCoffeeMaker {
  @Inject @Named("water") Heater waterHeater;
  @Inject @Named("hot plate") Heater hotPlateHeater;
}
           

通過搭配@ Provide注解方法來提供限定值。

@Provides @Named("hot plate") Heater provideHotPlateHeater() {
  return new ElectricHeater(70);
}

@Provides @Named("water") Heater provideWaterHeater() {
  return new ElectricHeater(93);
}
           

9.靜态注入(STATIC INJECTION)

警告:這個特性謹慎使用,靜态依賴關系難以測試和重用。

Dagger可以注入靜态屬性,使用@Inject注解聲明靜态屬性的類必須列在子產品的staticInjections 中。

@Module(
    staticInjections = LegacyCoffeeUtils.class
)
class LegacyModule {
}
           

使用ObjectGraph.injectStatics()方法給靜态屬性填值

ObjectGraph objectGraph = ObjectGraph.create(new LegacyModule());
objectGraph.injectStatics();
           

注意:靜态注入隻對直接圖中的子產品起作用。如果在調用plus()調用圖的injectStatics(),則不會執行對擴充圖中的子產品的靜态注入。

10.編譯時驗證 (COMPILE-TIME VALIDATION)

Dagger包括一個驗證子產品和注入的注解處理器。此處理器是嚴格的,如果任何綁定無效或不完整,将導緻編譯器錯誤。例如,此子產品缺少Executor的綁定:

@Module
class DripCoffeeModule {
  @Provides Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
  }
}
           

當編譯時,javac拒絕缺少的綁定:

Dagger1依賴注入

通過為Executor添加一個@Provides注解的方法或将子產品标記為不完整來修複此問題。不完整的子產品允許有缺失的依賴。

@Module(complete = false)
class DripCoffeeModule {
  @Provides Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
  }
}
           

列出的注入類型未使用的子產品會觸發錯誤。

@Module(injects = Example.class)
class DripCoffeeModule {
  @Provides Heater provideHeater() {
    return new ElectricHeater();
  }
  @Provides Chiller provideChiller() {
    return new ElectricChiller();
  }
}
           

因為在子產品中注入Example 隻使用Heater,是以javac拒絕沒有使用的綁定:

[ERROR] COMPILATION ERROR:
[ERROR]: Graph validation failed: You have these unused @Provider methods:
      1. coffee.DripCoffeeModule.provideChiller()
      Set library=true in your module to disable this check.
           

如果子產品綁定将在列出的注入對象之外使用,那麼将子產品标記為庫。

@Module(
  injects = Example.class,
  library = true
)
class DripCoffeeModule {
  @Provides Heater provideHeater() {
    return new ElectricHeater();
  }
  @Provides Chiller provideChiller() {
    return new ElectricChiller();
  }
}
           

充分利用編譯時驗證,建立一個包含所有應用程式子產品的子產品。注解處理器将跨子產品檢測問題并報告。

@Module(
    includes = {
        DripCoffeeModule.class,
        ExecutorModule.class
    }
)
public class CoffeeAppModule {
}
           

在編譯類路徑中包含Dagger的jar檔案時,将自動啟用注解處理器。

11.編譯時代碼生成(COMPILE-TIME CODE GENERATION)

編譯時代碼生成Dagger的注解處理器也可以生成名為CoffeeMaker I n j e c t A d a p t e r . j a v a 或 D r i p C o f f e e M o d u l e InjectAdapter.java或DripCoffeeModule InjectAdapter.java或DripCoffeeModuleModuleAdapter的源檔案。這些檔案是Dagger實作細節。

12.子產品重寫(MODULE OVERRIDES)

如果對于同一依賴有多個互相沖突的@Provides方法,Dagger注入失敗并出現錯誤。但是有時有必要用開發或測試的替代品來替代生産代碼。在子產品注解中使用overrides = true允許優先于其他子產品的綁定。

public class CoffeeMakerTest {
  @Inject CoffeeMaker coffeeMaker;
  @Inject Heater heater;

  @Before public void setUp() {
    ObjectGraph.create(new TestModule()).inject(this);
  }

  @Module(
      includes = DripCoffeeModule.class,
      injects = CoffeeMakerTest.class,
      overrides = true
  )
  static class TestModule {
    @Provides @Singleton Heater provideHeater() {
      return Mockito.mock(Heater.class);
    }
  }

  @Test public void testHeaterIsTurnedOnAndThenOff() {
    Mockito.when(heater.isHot()).thenReturn(true);
    coffeeMaker.brew();
    Mockito.verify(heater, Mockito.times(1)).on();
    Mockito.verify(heater, Mockito.times(1)).off();
  }
}
           

覆寫最适合應用程式的小變化:

用單元測試的模拟代替實際實作。

在開發中使用僞身份驗證替換LDAP身份驗證。

繼續閱讀