上一篇文章中讲了一个入门的例子,感觉虽然不懂内部怎么实现的,好像大体知道要怎么去用了,理解了每部分都是干什么的,既然都讲了例子了,那就继续用 这个例子讲下如果被依赖的类的构造函数带有参数,我们该怎么去处理?
现在大夏天的我们平时去吧台打咖啡都想来点清凉的,透心凉,心飞扬,那怎么办?这时候我们是不是该提供一个加冰块的功能啊?
咖啡小姐姐把咖啡豆进行研磨成粉,然后放到咖啡机中,用热水和加压器去从咖啡粉中流过,这样就出来一小杯原酿咖啡了...,然后可以加点牛奶啊,加点糖啊。
好了,既然要加冰块,那就加呗,但是冰块放到哪里? 咖啡机要有一个放冰块的空间吧?而且还不能让冰块放到这里那么热不融化了吗? 所以要有个小冰箱啊.
public interface Ice {
void add();
}
public interface IceBox {
void addIce();
}
public class NajiIce implements Ice{
public NajiIce() {
}
@Override
public void add() {
System.out.println("加冰了,心飞扬");
}
}
public class HaierIceBox implements IceBox {
Ice ice;
public IceBox(Ice ice) {
this.ice = ice;
}
@Override
public void addIce() {
ice.add();
}
}
设计完了,那按照我们平时的使用,改造一下CoffeeMaker
类:
class CoffeeMaker {
private final Heater heater;
private final Pump pump;
private final IceBox iceBox;
private Ice ice;
CoffeeMaker() {
heater = new ElectricHeater();
pump = new Thermosiphon();
ice = new NajiIce()
iceBox = new HaierIceBox(ice);
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
iceBox.addIce();
heater.off();
}
}
好了,这样就可以了,执行一下:
04-20 17:12:01.626 20240-20240/com.charon.stplayer I/System.out: ~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
加冰了,心飞扬
那通过dagger2
怎么使用呢?我们还要按照上一篇文章的步骤来:
- 首先来到
CoffeeModule
类中,让他把Ice
和IceBox
管理起来,一定要记得引入Ice
,因为IceBox
依赖了Ice
,这是一个依赖链条,要把这个链条上的所有依赖,都管理起来。
import dagger.Module;
import dagger.Provides;
@Module
public class CoffeeModule {
@Provides
Heater provideHeater() {
return new ElectricHeater();
}
@Provides
Pump providePump() {
return new Thermosiphon();
}
@Provides
Ice provideIce() {
return new NanjiIce();
}
@Provides
IceBox provideIceBox(Ice ice) {
return new HaierIceBox(ice);
}
}
- 修改
CoffeeComponent
类,增加Ice
和IceBox
对应的方法:
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
void inject(CoffeeMaker maker);
Heater provideHeater();
Pump providePump();
Ice provideIce();
IceBox provideIceBox(Ice ice);
}
- 在目标类中增加注入
class CoffeeMaker {
@Inject
Heater heater;
@Inject
Pump pump;
@Inject
IceBox iceBox;
CoffeeMaker() {
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
iceBox.addIce();
heater.off();
}
}
写的很明白了,就是Compnonet
中声明的方法不能带参数。
好,我们修改一下把provideIceBox(Ice ice)
的ice
参数去掉:
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
void inject(CoffeeMaker maker);
Heater provideHeater();
Pump providePump();
Ice provideIce();
IceBox provideIceBox();
}
好了,再运行一下:
04-20 17:30:07.125 20916-20916/com.charon.stplayer I/System.out: ~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
加冰了,心飞扬
完美
总结一下:
如果被依赖类的构造函数带有参数,要把这个参数的类型也管理起来,而且Component
接口中的方法不能带参数。
那么问题又来了,前面我们讲的例子都是依赖类只有一个构造函数,直接反射创建就好了,假如我们的依赖类有多个构造函数呢?该如何去选择根据不同的构造函数 生成对应的对象呢?
假设我们的咖啡机里又加新功能了,只加冰虽然凉了,但是口感不够丝滑,想再加点牛奶,享受丝滑般的感受。 好了新建一个牛奶类:
public class Milk {
String type;
public Milk() {
type = "";
}
public Milk(String shuiguo) {
type = shuiguo;
}
public void addMilk() {
System.out.println("添加:" + type + "牛奶");
}
}
接下来要用到一个比较重要的东西就是限定符,用来区分哪个构造函数new
出来的对象:
@Qualifier//限定符
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Type {
String value() default "";//默认值为""
}
这里插入一句,上面我们是使用@Qualifier
自定义了一个限定符功能,其实这种返回值相同的情况是比较常见的,Dagger2
中已经为我们提供了类似的自定义限定符@Named
,我们可以直接使用@Named
,
这里看一下它的实现:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
看到了吗? 和我们上面定义的Type
注解一模一样。
接下来就是修改Moudle
类,并且用上面我们创建的限定符@Type
来区分不同的构造函数new
出来的对象:
@Module
public class CoffeeModule {
@Provides
Heater provideHeater() {
return new ElectricHeater();
}
@Provides
Pump providePump() {
return new Thermosiphon();
}
@Provides
Ice provideIce() {
return new NanjiIce();
}
@Provides
IceBox provideIceBox(Ice ice) {
return new HaierIceBox(ice);
}
@Provides
@Type("normal")
Milk provideNormalMilk() {
return new Milk();
}
@Provides
@Type("shuiguo")
Milk provideShuiGuoMilk(String shuiguo) {
return new Milk(shuiguo);
}
// 由于Milk构造函数里使用了String,所以这里要管理这个String(★否则报错)
// int等基本数据类型是不需要这样做的
@Provides
public String provideString() {
return new String();
}
}
接下来继续修改Component
类:
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
void inject(CoffeeMaker maker);
Heater provideHeater();
Pump providePump();
Ice provideIce();
IceBox provideIceBox();
// 添加新提价的方法,注意这里也要用@Type声明,因为是通过@Type的值来去找不同的构造函数创建对象的
@Type("normal")
Milk provideNormalMilk();
@Type("shuiguo")
Milk provideShuiGuoMilk();
String provideString();
}
我们在CoffeeMaker
里面修改对应的代码:
import javax.inject.Inject;
class CoffeeMaker {
@Inject
Heater heater;
@Inject
Pump pump;
@Inject
IceBox iceBox;
@Inject
@Type("shuiguo") // 使用@Type来制定对应的构造函数
Milk shuiguoMilk;
CoffeeMaker() {
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
iceBox.addIce();
shuiguoMilk.addMilk();
heater.off();
}
}
执行结果:
04-20 19:07:53.823 23151-23151/? I/System.out: ~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
加冰了,心飞扬
添加:牛奶
虽然是可以了,但是问题又来了,我想在创建Milk(String shuiguo)
时往里面传递参数,例如,我传递个caomei
。这该如何操作呢?
其实执行过程是这样的,再去Module
中执行构造函数时候Milk(String shuiguo)
,他会发现可以传一个参数,然后他就会在该Module
中去找返回值是string
的方法,
并将该值传递给构造函数的参数中。
现在,我们重新把上面的CoffeeModule
修改一下:
@Module
public class CoffeeModule {
@Provides
Heater provideHeater() {
return new ElectricHeater();
}
@Provides
Pump providePump() {
return new Thermosiphon();
}
@Provides
Ice provideIce() {
return new NanjiIce();
}
@Provides
IceBox provideIceBox(Ice ice) {
return new HaierIceBox(ice);
}
@Provides
@Type("normal")
Milk provideNormalMilk() {
return new Milk();
}
@Provides
@Type("shuiguo")
Milk provideShuiGuoMilk(String shuiguo) {
return new Milk(shuiguo);
}
// 由于Milk构造函数里使用了String,所以这里要管理这个String(★否则报错)
// int等基本数据类型是不需要这样做的
@Provides
public String provideString() {
return "caomei"; // 前面我们写的是new String(),这里改成caomei
}
}
好了,执行下:
04-20 19:28:31.944 23549-23549/? I/System.out: ~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
加冰了,心飞扬
添加:caomei牛奶
看,参数加上去了。
下一篇:4.Dagger2单例(四)
- 邮箱 :charon.chui@gmail.com
- Good Luck!