Dagger2入门demo(二)
Dagger中使用了很多注解:
@Module:Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)@Provide:在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。@Singleton:当前提供的对象将是单例模式 ,一般配合@Provides一起出现@Component:用于接口,这个接口被Dagger2用于生成用于模块注入的代码@Inject:在需要依赖的地方使用这个注解。(你用它告诉Dagger这个构造方法,成员变量或者函数方法需要依赖注入。这样Dagger就会构造一个这个类的实例并满足他们的依赖。)@Scope:Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。
这个东西不太好入门,这里就直接用例子上代码了。有关如何添加以来在github上面写的很清楚了,android在build.gradle中进行配置就好了。
官方demo描述的是:一个泵压式咖啡机(CoffeeMaker)由两个主要零件组成,泵浦(Pump)和加热器(Heater),咖啡机有一个功能是煮泡咖啡(brew),
当进行煮泡咖啡时,会按如下几个步骤进行:
- 打开加热器进行加热
- 泵浦加压
- 萃取出咖啡
- 然后关闭加热器
一杯咖啡就算制作完毕了。
好了,那我们先开始写代码了(我把官方的示例代码进行了简化,方便更好的理解):
定义加热器接口:
interface Heater {
void on();
void off();
}
具体实现类:
class ElectricHeater implements Heater {
@Override
public void on() {
System.out.println("~ ~ ~ heating ~ ~ ~");
}
@Override
public void off() {
}
}
定义泵浦接口:
interface Pump {
void pump();
}
具体实现类:
class Thermosiphon implements Pump {
Thermosiphon() {
}
@Override
public void pump() {
System.out.println("=> => pumping => =>");
}
}
咖啡机功能:
class CoffeeMaker {
private final Heater heater;
private final Pump pump;
CoffeeMaker() {
heater = new ElectricHeater();
pump = new Thermosiphon();
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
heater.off();
}
}
好了完成了,如果执行的话运行结果是:
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
完美,上面是我们普通的开发方式,那如果我们要用dagger来实现该怎么弄呢?
首先这几个类是没有改动的,因为这几个类中没有牵扯到其他的类:
interface Pump {
void pump();
}
interface Heater {
void on();
void off();
}
class ElectricHeater implements Heater {
@Override
public void on() {
System.out.println("~ ~ ~ heating ~ ~ ~");
}
@Override
public void off() {
}
}
下面就开始使用dagger需要改动的部分,首先创建module类。@Module类的命名惯例是以Module作为类名称的结尾。而@Module类中的@Provides方法名称的命名惯例是以provide作为前缀。
而这个Module是干什么的呢?Module管理所有的以来,这里你做咖啡需要以来泵浦(Pump)和加热器(Heater),所以可以创建Module把他们Pump和Heater管理起来,方便
之后获取Pump和Heater对象,所以它里面会有provideXXX()的函数。
- 第一步:增加
Module类,使用@Module声明类(可以将Module理解成生产对象的工厂,里面提供了provideXXX这种生产对象的方法)
@Module // Module注明该类是Module类
public class CoffeeModule {
@Provides // Provides注明该方法是用来提供依赖对象的方法
Heater provideHeater() {
return new ElectricHeater();
}
@Provides
Pump providePump() {
return new Thermosiphon();
}
}
- 第二步:增加接口
Component,使用@Component声明(Componet是将Module中生产的对象注入到对应的需要使用该对象的类中)
// 这里是声明要从CoffeeModule中去找对应的依赖,从`CoffeeModule`中去通过`provideXXX`方法来获取对应的对象
@Component(modules = CoffeeModule.class)
// 该接口会在编译时自动生成对应的实现类,这里是DaggerCoffeeComponent
public interface CoffeeComponent {
// 提供一个供目标类使用的注入方法,该方法表示要将Module中的管理类注入到哪个类中,这里当然是CoffeeMaker,因为我们要用他俩去生产咖啡
void inject(CoffeeMaker maker);
}
注意:@Component(modules = {AModule.class,BModule.class})可以设置多个Module,而且也可以@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
指定依赖的Module和父Component
- 第三步:
Inject注入
import javax.inject.Inject;
class CoffeeMaker {
@Inject // 标记该对象是被注入的
Heater heater;
@Inject
Pump pump;
CoffeeMaker() {
// DaggerCoffeeComponent这个类会在编译时产生,所以可以build一下
CoffeeComponent component = DaggerCoffeeComponent.create();
// 注入
component.inject(this);
// 或者使用下面的代码也是可以的
// DaggerCoffeeComponent.builder()
// .coffeeModule(new CoffeeModule())
// .build()
// .inject(this);
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
heater.off();
}
}
到这里就完成了,执行的话,结果是一样的:
04-20 15:16:55.083 16375-16375/com.charon.stplayer I/System.out: ~ ~ ~ heating ~ ~ ~
04-20 15:16:55.084 16375-16375/com.charon.stplayer I/System.out: => => pumping => =>
[_]P coffee! [_]P
到这里你肯定就迷糊了,为什么? 本来很简单的东西,你还多搞出来几个类,弄的这么麻烦。但是麻烦吗? 你要是有十个地方呢? 一百个地方呢? 以后你修改怎么改? 一百个地方都改吗?
好了,最后来个总结,高中老师讲的,要来个综上所述:
- 通过
@Module声明一个XXXModule类,用于管理所有依赖,并使用@Provides为每个依赖提供provideXXX()方法 - 通过
@Component声明一个XXXComponent接口,并且声明要从哪些Module中寻找依赖
- 声明要去哪些
Module中寻找依赖
@Component(modules = CoffeeModule.class) - 提供一个供目标类使用的注入方法
void inject(CoffeeMaker maker); - 为所有的依赖添加一个方法
这个说起来就有点多了,而且我们上面的代码中并没有进行这一步操作,这是因为方法也可以不写,但是如果要写,就按照这个格式来
但是当Component要被别的Component依赖时,这里就必须写这个方法,不写代表不向别的Component暴露此依赖,而且如果要写的话
这些方法返回值必须是从上面指定的依赖库CoffeeModule.class中取得的对象,注意:而方法名不一致也行,但是方便阅读,建议一致,因为它主要是根据返回值类型来找依赖的。
这里如果要写的话就是
java
Heater provideHeater();
Pump providePump();
- 声明要去哪些
- 目标类使用依赖注入,首先是使用
@Inject声明属性变量,表示注入这个依赖,然后是调用inject()方法注入依赖。
下面来个图,能够更方便的去理解:
上图中:
- Container就是可以被注入的容器,具体对应上文例子中的CoffeeMaker,Container拥有需要被初始化的元素。需要被初始化的元素必须标上@Inject,只有被标上@Inject的元素才会被自动初始化。@Inject在Dagger2中一般标记构造方法与成员变量。
- Module可以说就是依赖的原材料的制造工厂,所有需要被注入的元素的实现都是从Module生产的。
- 有了可以被注入的容器Container,也有了提供依赖对象的Module。我们必须将依赖对象注入到容器中,这个过程由Component来执行。Component将Module中产生的依赖对象自动注入到Container中。
好像大体明白了点….
上面有DaggerBaseComponent.create();和DaggerBaseComponent.builder().build()两种方式有什么区别呢? 我们看一下源码:
public static Builder builder() {
return new Builder();
}
public static BaseComponent create() {
return new Builder().build();
}
Dagger2会自动生成该接口的实现类(以Dagger开头,如果@Component注解类不是顶级类,自动生成的实现类的名字会闭包命名并用下划线分割如DaggerFoo_Bar_BazComponent),可以通过该实现类的builder()方法获得builder对象,builder对象可以设置好依赖,最后通过build()方法构建实例:
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
如果Modules都是缺省构造器,@Provides方法都是静态的,用户不需要构建依赖实例,那么自动生成的实现类就会有create()方法获取新实例,就不需要处理builder了。
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
你的star是我的动力!!!

本文详细介绍Dagger2依赖注入的基本概念与应用实践,通过咖啡机示例展示如何使用@Module、@Provide等注解管理依赖关系。
3453

被折叠的 条评论
为什么被折叠?



