Dagger:快速的依赖注入for 安卓&Java

Dagger:快速的依赖注入for 安卓&Java

2014年5月8日 星期四

15:29

官网: http://square.github.io/dagger/
GitHub https://github.com/square/dagger

JavaDocs http://square.github.io/dagger/javadoc/index.html

 注:来自我的OneNote笔记,其实官网给的教程挺好的,最好下载GitHub上的例子来看

 

使用方法:

例子: coffee example.

 

声明依赖性:使用javax.inject.Inject注解。

注解构造方法:

class Thermosiphon implements Pump {

  private final Heater heater;

 

  @Inject

  Thermosiphon(Heater heater) {

    this.heater = heater;

  }

 

  ...

}

当然,Dagger支持直接注解成员变量:

class CoffeeMaker {

  @Inject Heater heater;

  @Inject Pump pump;

 

  ...

}

如果你的类有通过@Inject注解的成员变量,但是没有@Inject注解的构造方法,Dagger就会使用一个无参构造方法(如果存在的话)。

Dagger不支持方法注入

满足依赖关系:

默认情况下,Dagger满足依赖关系是通过调用构造方法得到的实例,比如如果要一个CoffeeMaker对象,Dagger就会调用new CoffeeMaker(),并且设置所有可注入的成员变量。

但是,下面的情况下,@Inject是不能用的:

  1. 接口
  2. 第三方的类
  3. 可配置的对象必须被配置。应该值得是那些经过配置才得到实例的类

这时候,我们就可以通过@providers,去注解一个方法来满足依赖。方法的返回类型就是依赖要满足的类型。

如通过provideHeater()来得到一个Heater:

@Provides Heater provideHeater() {

  return new ElectricHeater();

}

 

也有可能@Providers方法必须依赖他自己:

@Provides Pump providePump(Thermosiphon pump) {

  return pump;

}

 

所有的@Providers方法必须属于一个module,通过对一个类进行@Module注解来实现。

@Module

class DripCoffeeModule {

  @Provides Heater provideHeater() {

    return new ElectricHeater();

  }

 

  @Provides Pump providePump(Thermosiphon pump) {

    return pump;

  }

}

按照惯例 @Providers方法都会用provide作为前缀,@Module类都用Module作为后缀。

建立一个Graph

创建一个ObjectGraph对象的方法是调用create()并传入一个Module

ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());

为了使用,我们需要指定模块的注入对象类。这里我们将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);

    ...

  }

}

然后,我们配置@Module注解来注册之。

@Module(

    injects = CoffeeApp.class

)

class DripCoffeeModule {

  ...

}

单例:@Singleton

//1. 标记方法

@Provides @Singleton Heater provideHeater() {

  return new ElectricHeater();

}

//2. 标记类

@Singleton

class CoffeeMaker {

  ...

}

懒注入:Lazy

class GridingCoffeeMaker {

  @Inject Lazy<Grinder> lazyGrinder;

 

  public void brew() {

    while (needsGrinding()) {

      // 调用get方法的时候Grinder的实例才被创建

      lazyGrinder.get().grind();

    }

  }

}

Provider 注入:每次调用get都得到一个新的实例

class BigCoffeeMaker {

  @Inject Provider<Filter> filterProvider;

 

  public void brew(int numberOfPots) {

    ...

    for (int p = 0; p < numberOfPots; p++) {

      maker.addFilter(filterProvider.get()); //new filter every time.

      maker.addCoffee(...);

      maker.percolate();

      ...

    }

  }

}

Qualifiers:限定符,通过限定符来得到不同限定符的实例

@Qualifier

@Documented

@Retention(RUNTIME)

public @interface Named {

  String value() default "";

}

你可以创建自定义的Qualifier或者使用Named

class ExpensiveCoffeeMaker {

  @Inject @Named("water") Heater waterHeater;

  @Inject @Named("hot plate") Heater hotPlateHeater;

  ...

}

Qualifier也支持@Providers

@Provides @Named("hot plate") Heater provideHotPlateHeater() {

  return new ElectricHeater(70);

}

 

@Provides @Named("water") Heater provideWaterHeater() {

  return new ElectricHeater(93);

}

静态注入:谨慎使用,不好测试和重复使用

 

编译阶段的校验:

Dagger包含一个注解处理器来校验ModulesInjections,这个processor是严格的,所以如果你的dagger使用有误的话会导致编译器错误。例如下面这个问题会提示错误:

@Module

class DripCoffeeModule {

  @Provides Heater provideHeater(Executor executor) {

    return new CpuHeater(executor);

  }

}

编辑错误信息为:

[ERROR] COMPILATION ERROR :

[ERROR] error: No binding for java.util.concurrent.Executor

               required by provideHeater(java.util.concurrent.Executor)

解决办法是提供一个得到Executor的方法,并且用@Providers注解,或者标记该modulecomplete=false,意思是可以允许丢失的依赖:

@Module(complete = false)

class DripCoffeeModule {

  @Provides Heater provideHeater(Executor executor) {

    return new CpuHeater(executor);

  }

}

如果Module中提供了没有用到的注入也会报错,如:

@Module(injects = Example.class)

class DripCoffeeModule {

  @Provides Heater provideHeater() {

    return new ElectricHeater();

  }

  @Provides Chiller provideChiller() {

    return new ElectricChiller();

  }

}

因为Example类只用到了Heaterjavac就会报错:

[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 列表以外的类使用的话,可以标注module为一个library,方法如下:

@Module(

  injects = Example.class,

  library = true

)

class DripCoffeeModule {

  @Provides Heater provideHeater() {

    return new ElectricHeater();

  }

  @Provides Chiller provideChiller() {

    return new ElectricChiller();

  }

}

我们可以创建一个模块,这个模块包含了所有其他的模块Module

@Module(

    includes = {

        DripCoffeeModule.class,

        ExecutorModule.class

    }

)

public class CoffeeAppModule {

}

Dagger的注解处理器会在编译的时候生成类似CoffeeMaker$InjectAdapter.java或者DripCoffeeModule$ModuleAdater这样的代码文件,这些文件是Dagger的实现。你不要直接使用他们,当然,Debug的时候,就很方便了。

Dagger会报错,如果一个依赖中包含多个同样的@Providers方法。但是有时候我们也需要这样做,比如在开发或者测试的时候,可以使用overvides=true标记Module来实现这一目的。

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();

  }

}

重写在下面这样的情景很适合用:

单元测试时,用一个模拟的实现替换一个真是的实现

在开发时,用一个模拟的认证替换一个真实的认证。

集成方法:

Maven

<dependency>

  <groupId>com.squareup.dagger</groupId>

  <artifactId>dagger</artifactId>

  <version>(insert latest version)</version>

</dependency>

<dependency>

  <groupId>com.squareup.dagger</groupId>

  <artifactId>dagger-compiler</artifactId>

  <version>(insert latest version)</version>

  <optional>true</optional>

</dependency>

 

 

Gradle类似,语法是 compile "groupId:artifactId:version"

posted @ 2014-07-29 00:45  天外的星星  阅读(1529)  评论(0编辑  收藏  举报