码迷,mamicode.com
首页 > 其他好文 > 详细

Dagger2官方CoffeeMaker案例的分解说明

时间:2015-08-27 02:15:15      阅读:591      评论:0      收藏:0      [点我收藏+]

标签:

官方例子的场景描述:一个泵压式咖啡机(CoffeeMaker)由两个主要零件组成,泵浦(Pump)和加热器(Heater),咖啡机有一个功能是煮泡咖啡(brew),当进行煮泡咖啡时,会按如下几个步骤进行打开加热器进行加热,泵浦加压,萃取出咖啡,然后关闭加热器,一杯咖啡就算制作完毕了。

按照上一篇文章的5个步骤,我们来分解一下这个例子:

Step 1 确定依赖和被依赖对象

依赖对象是CoffeeMaker对象,被依赖对象是Heater类型对象和Pump类型对象。

src/coffee/CoffeeMaker.java

class CoffeeMaker {

 private final Heater heater;

 private final Pump pump;

 

 CoffeeMaker(Heater heater, Pump pump) {

   this.heater = heater;

   this.pump = pump;

  }

 

 public void brew() {

   heater.on();

   pump.pump();

   System.out.println(" [_]P coffee! [_]P ");

   heater.off();

  }

}
src/coffee/Heater.java
interface Heater {

 void on();

 void off();

 boolean isHot();

}
src/coffee/Pump.java
interface Pump {

 void pump();

}
与上一个例子不同的是,这里Heater和Pump都是接口。既然是接口,就意味着必须要有对应的实现类才可以创建出相应的对象。ElectricHeater和Heater接口的实现类,Thermosiphon是Pump接口的实现类:

src/coffee/ElectricHeater.java

class ElectricHeater implements Heater {
  boolean heating;

  @Override public void on() {
    System.out.println("~ ~ ~ heating ~ ~ ~");
    this.heating = true;
  }

  @Override public void off() {
    this.heating = false;
  }

  @Override public boolean isHot() {
    return heating;
  }
}
src/coffee/Thermosiphon.java
class Thermosiphon implements Pump {

 private final Heater heater;

 

 Thermosiphon(Heater heater) {

   this.heater = heater;

  }

 

  @Overridepublic void pump() {

   if (heater.isHot()) {

     System.out.println("=> => pumping => =>");

    }

  }

}
Step 2. 创建@Module类

@Module类的命名惯例是以Module作为类名称的结尾。而@Module类中的@Provides方法名称的命名惯例是以provide作为前缀。

官方案例在这里使用了2个@Module类,一个@Module类作为了另一个@Module类的组成部分。

src/coffee/DripCoffeeModule.java

@Module(includes = PumpModule.class)

class DripCoffeeModule {

 @Provides @Singleton Heater provideHeater() {

   return new ElectricHeater();

  }

}
@Module注解使用了一个属性include,将PumpModule作为了自身的一部分。

src/coffee/PumpModule.java

@Module

class PumpModule {

 @Provides Pump providePump(Thermosiphon pump) {

   return pump;

  }

}
Step 3. 获得了@Module类之后,对Step1中的需要注入的地方进行相应的注解。
class CoffeeMaker {

 private final Lazy<Heater> heater;

 private final Pump pump;

 

  @InjectCoffeeMaker(Lazy<Heater> heater, Pump pump) {

   this.heater = heater;

   this.pump = pump;

  }

 

 public void brew() {

   heater.get().on();

   pump.pump();

   System.out.println(" [_]P coffee! [_]P ");

   heater.get().off();

  }

}
这里仍然使用为构造器添加@Inject注解的方式,到时在创建CoffeeMaker对象的时候,Dagger2会自动调用这个带@Inject的构造器,同时根据@Module去获得需要传入构造器的Heater类型对象与Pump类型对象。另外,这里对heater属性采用了“延迟加载”机制,即Heater类型对象的真正实例化是在第一次调用heater.get()方法的时候进行(因为此时heater是Lazy<Heater>类型,因此要先调用get方法来获得Lazy<Heater>中封装的Heater对象,进而才能调用Heater对象的方法)。

@Module类中,provideHeater方法提供Heater类型对象的时候,是直接显示的调用ElectricHeater对象的构造器来进行的,因此不存在什么注入不注入的事情,即ElectricHeater不用进行任何改写。而providePump方法在提供Pump类型对象的时候,是把方法的形参作为返回值返回的,为了保证Dagger2在创建CoffeeMaker对象的时候,可以自动装配pump属性,因此必须要为Thermosiphon类的构造器添加@Inject注解,没有@Inject注解的类,Dagger2是不认识的,更无法自动进行构造器的调用创建实例。

src/coffee/Thermosiphon.java

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 => =>");

    }

  }

}
Step 4. 创建一个接口,让@Inject和@Module建立起联系

经过Step2和Step3对Step1中创建的类的修改,实际上CoffeeMaker的依赖关系已经描述完毕了,接下来就是要创建一个@Commpoment接口,通过接口中的一个无参数方法来“驱使”Dagger2来创建出一个CoffeeMaker对象,并且随着这个对象的建立,CoffeeMaker中所有依赖对象都装配好。官方例子中这个接口被作为一个内部接口的方式提供:

src/coffee/CoffeeApp$Coffee.java

@Singleton

 @Component(modules = { DripCoffeeModule.class })

 public interface Coffee {

   CoffeeMaker maker();

  }
Step 5. 利用Dagger2自动生成的@Commpoment接口实现类创建Coffee类型的实例。

一旦利用Dagger2自动生成的@Commpoment接口实现类创建出Coffee类型的实例,就可以调用maker方法获得CoffeeMaker类型的实例。此时这个CoffeeMaker实例中所有的依赖关系都被装配好了。官方例子中,是这样写的:

src/coffee/CoffeeApp.java

public class CoffeeApp {

 @Singleton

 @Component(modules = { DripCoffeeModule.class })

  publicinterface Coffee {

   CoffeeMaker maker();

  }

 public static void main(String[] args) {

   Coffee coffee = DaggerCoffeeApp_Coffee.builder().build();

   coffee.maker().brew();

  }

}
@Component接口作为CoffeeApp的内部接口来呈现。那么此时Dagger2为该接口自动生成的实现类名称就是Dagger外部类名称_接口名称,即DaggerCoffeeApp_Coffee。然后再调用相应的builder方法来创建出Coffee实例。官方的例子在创建Coffee类型的实例时,使用的是DaggerCoffeeApp_Coffee.builder().build(),如果完全按照上一个例子的写法,将该方法写完整DaggerCoffeeApp_Coffee.builder().dripCoffeeModule(newDripCoffeeModule()).build();效果是完全一样的,这一点通过查看一下DaggerCoffeeApp_Coffee的源码即可得知。而且,如果@Module类实例的创建是通过默认无参构造器来创建的,那么可以不使用Build模式,将这简写为DaggerCoffeeApp_Coffee.create()即可。

另外,如果DaggerCoffeeApp_Coffee已经生成,对@Inject或者@Module做出改动不会影响到DaggerCoffeeApp_Coffee,但是对@Commpoment接口或者DaggerCoffeeApp_Coffee调用方法的相关代码做出改动,Eclipse中都会对DaggerCoffeeApp_Coffee类报错。此时要强行对项目进行一次重建(rebuild)。但是Eclipse是没有rebuild这个功能键的。要在Eclipse中对一个项目进行rebuild的方式可以参考“在Eclipse中搭建Dagger和Dagger2使用环境”的做法,先将Annotation Processing的Enable projectspecific settings选项对勾去掉,单击“apply”,此时Eclipse会有一个提示,点击yes即可对项目进行一次rebuild,此时rebuild的结果肯定是错误的,然后在重新将Annotation Processing的Enable projectspecific settings选项对勾选中,单击“apply”,此时Eclipse会再次出现一个提示,点击yes对项目再进行一次rebuild,此时就可以得到正确的DaggerCoffeeApp_Coffee类了。

最后,在Step2中,如果不对@Module注解使用includes属性,完全可以将DripCoffeeModule和PumpModule合并成一个@Module类:

src/coffee/NewDripCoffeeModule.java

@Module

public class NewDripCoffeeModule {

         @Provides@Singleton Heater providHeater(){

                  returnnew ElectricHeater();

         }

         @Provides@Singleton Pump providePump(Heater heater){

                  returnnew Thermosiphon(heater);

         }

}
在providePump方法中返回Thermosiphon对象的时候,因为构造器需要一个Heater类型的对象,因此把这个Heater类型的对象从方法参数传进去,到时候Dagger2在找这个Heater类型对象的时候,自然会去根据providHeater方法来自动装配一个ElectricHeater实例进去。这一点可以参考DaggerCoffeeApp_Coffee类的源码,看看它是如何处理@Module类中的@Provides方法的。

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

Dagger2官方CoffeeMaker案例的分解说明

标签:

原文地址:http://blog.csdn.net/piglite/article/details/48017187

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!