zoukankan      html  css  js  c++  java
  • 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"

  • 相关阅读:
    mvn 创建的项目 导入到eclipse
    maven GroupID和ArtifactID
    eclipse配置maven + 创建maven项目
    微服务简介
    spring-boot5代码
    spring-boot5
    TextView及其子类
    RTMP协议
    实现输出h264直播流的rtmp服务器
    Android按键事件传递流程(二)
  • 原文地址:https://www.cnblogs.com/leo-lsw/p/dagger.html
Copyright © 2011-2022 走看看