依赖注入
依赖注入:在项目开发中,常见一个类的实例依赖另一个类实例的情况 如
public class Man(){
prviate Car car;
public Man(){
car=new Car();
}
}
常见的依赖方式基于构造方法,set设置等
依赖注入是控制反转的一种表现形式,被依赖的对象(Car)不再由 依赖对象(Man)主动创建 ,而是由第三方容器去创建,然后注入到依赖对象(man)的过程,降低代码耦合性
集成与使用
在build.gradle中添加依赖:
dependencies {
...
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
}
借助上面的场景进行应用
Car相关代码 由于Car需要被注入,在构造方法上增加 @Inject 注解
public class Car {
private CarDipan carDipan;
@Inject
public Car() {
}
public void pao() {
System.out.println("跑起来");
}
}
Man相关代码 在Car变量上声明 @Inject 注解
public class Man {
@Inject
Car car;
public Man() {
//该行代码后面讲到
DaggerManComponent.create().inject(this);
}
public void kaiche() {
car.pao();
}
}
此时依赖和被依赖的类都创建好了,还缺少一个纽带把他们连接起来,纽带就是 @Component
@Component 注解需要作用到接口或者抽象类上,添加抽象方法,参数是需要注入依赖的类,会自动生成以Dagger开头的实现类
在Man的构造方法中进行注入 DaggerManComponent.create().inject(this); (可能会报找不到该类,需要rebuild下)
ManComponent代码如下
@Component
interface ManComponent {
void inject(Man man);
}
测试代码
public class DaggerTest {
public static void main(String[] args) {
Man man = new Man();
man.kaiche();
}
}
Module和Provides注解
有一些第三方库,我们无法修改源码,即无法用@Inject去注解构造方法。这时候就需要@Module和@Provides注解
- @Module 注解到类上,表示这个类可以作为一个模块去引入到被@Component注解的接口上,规定用@Module注解的类名以Module做后缀
- @Provides用于被@Module标注类的方法中,规定用provide作为前缀
假设Man对象包含一个FangZi对象,该对象构造方法无法用@Inject注解标注,那么可以提供以下类
@Module
public class FangModule {
@Provides
public Fang provideFang() {
return new Fang();
}
}
对应的ManComponent类也需要修改
@Component(modules = FangModule.class)
interface ManComponent {
void inject(Man man);
}
这样Man里面的房子属性就会成功注入。被注入的属性一定要用@Inject注解
Module类的构造方法可以带有参数,需要注意的是当Module类有带有参数的构造方法时,
DaggerManComponent不会有Create方法,需要通过DaggerManComponent.builder().fangModule(new FangModule(参数)).build()手动传入module对象
Bind注解
Bind注解和Provides注意类似 ,也是放到被Module类的方法上,不同的是Bind注解的方法是抽象方法,参数类型是返回值类型的子类。 用于注入某个接口或者抽象类时,可以获取他的实现子类。或者可以说将一种类型转换成他的父类或接口,只暴露父类或接口的方法
BindModule.java
@Module
public abstract class BindModule {
@Binds
public abstract Manger bindManager(ManagerImpl manager);
public static abstract class Manger {
public abstract String getName();
}
public static class ManagerImpl extends Manger {
@Inject
public ManagerImpl() {
}
@Override public String getName() {
return "我是ManagerImpl";
}
}
}
Man.java
public class Man {
@Inject
BindModule.Manger manger;//这是获取的时他的子类MangerImpl
public Man() {
DaggerManComponent.create().inject(this);
}
public void run() {
System.out.println(manger.getName());
}
}
Named、Singleton和Reusable
@Named 别名注解 和@Provides配合使用 比如我有两套房子,但是返回值都是房子 如果不用@Named注解,dagger不知道咱们要哪个房子,就会迷失,这时就需要起一个别名
@Module
public class FangModule {
@Named("北三环")
@Provides
public Fang provideFang() {
return new Fang("北三环");
}
@Named("西四环")
@Provides
public Fang provideFang2() {
return new Fang("西四环");
}
}
在获取房子是就必须注明@Named 表示 要哪一个
public class Man {
@Inject
Car car;
@Named("北三环")
@Inject
Fang fang1;
@Named("西四环")
@Inject
Fang fang2;
}
@Qualifier 元注解 注解在@Named上 我们可以用 Qualifier自定义和 @Named功能相同的 注解
@Singleton 单例注解 在一个Component组件中单例 Singleton标注在Component上 和 Provides标注的方法或者标注在要注入的类上。
实现机制 是通过DoubleCheck来保证单例,在生成的DaggerManComponent类的初始化中,会把Provider转换为DoubleCheck
在DoubleCheck的get方法中保证实例唯一
@Module
public class FangModule {
@Singleton
@Provides
public Fang provideFang() {
return new Fang("北三环");
}
}
@Singleton
@Component(modules = FangModule.class)
interface ManComponent {
void inject(Man man);
}
@Scope 元注解 注解在@Singleton 我们可以用 @Scope自定义和 @Singleton功能相同的 注解
@Reusable 注解 有时我们为了限制对象的创建次数,使用时可以从缓存中获取对象,可以使用该注解 该注解标注在 Provides标注的方法或者标注在要注入的类上。
该机制会把Provider转换为SingleCheck来 使用SingleCheck来保证如果无对象会创建对象 如果有则会从缓存中获取
@Module
public class FangModule {
@Reusable
@Provides
public Fang provideFang() {
return new Fang("北三环");
}
}
@Component(modules = FangModule.class)
interface ManComponent {
void inject(Man man);
}
Lazy和Provider
Lazy 有时候我们不想运行时就把类创建好,而是在需要的时候创建类,这是就可以通过Lazy包装要被注入的成员 ,使用时通过get方式获取
public class Man {
@Inject
Lazy<Car> car;
public Man() {
DaggerManComponent.create().inject(this);
}
public void kaiche() {
car.get().pao();
}
}
Provider 有时我们 需要创建类的多个实例,可以通过Provider包装要被注入的成员,每次执行get则会创建一个新的实例
IntoSet和IntoMap注解
IntoSet需要和Provides或Bind一起使用,我们知道在Module中如果有两个方法返回值会导致依赖迷失,需要用Named注解起别名。 但是如果这些方法被@IntoSet注解修饰,相同的返回值产生的对象会被放到同一个Set集合中。同时注入该类对象时,需要用Set去包装
FangModule.java
@Module
public class FangModule {
@IntoSet
@Provides
public Fang provideFang1() {
return new Fang("国贸");
}
@IntoSet
@Provides
public Fang provideFang2() {
return new Fang("望京");
}
}
Man.java
public class Man {
@Inject
Set<Fang> fangs;
public Man() {
DaggerManComponent.builder().fangModule(new FangModule()).build().inject(this);
}
public void run() {
fangs.forEach(Fang::get);
}
}
IntoMap和IntoSet类似,不过是将发返回的对象放入到map集合中。用IntoMap注解时, 必须指明Key.使用 @IntKey/ @LongKey/@StringKey注解 也可以使用@MapKey元注解,自定义Key注解