依赖注入:在项目开发中,常见一个类的实例依赖另外一个类实例的状况 如java
public class Man(){
prviate Car car;
public Man(){
car=new Car();
}
}
复制代码
常见的依赖方式基于构造方法,set设置等缓存
依赖注入是控制反转的一种表现形式,被依赖的对象(Car)再也不由 依赖对象(Man)主动建立 ,而是由第三方容器去建立,而后注入到依赖对象(man)的过程,下降代码耦合性bash
在build.gradle中添加依赖:ide
dependencies {
...
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
}
复制代码
借助上面的场景进行应用测试
Car相关代码 因为Car须要被注入,在构造方法上增长 @Inject 注解gradle
public class Car {
private CarDipan carDipan;
@Inject
public Car() {
}
public void pao() {
System.out.println("跑起来");
}
}
复制代码
Man相关代码 在Car变量上声明 @Inject 注解ui
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代码以下this
@Component
interface ManComponent {
void inject(Man man);
}
复制代码
测试代码google
public class DaggerTest {
public static void main(String[] args) {
Man man = new Man();
man.kaiche();
}
}
复制代码
有一些第三方库,咱们没法修改源码,即没法用@Inject去注解构造方法。这时候就须要@Module和@Provides注解spa
@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注解和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 别名注解 和@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 有时候咱们不想运行时就把类建立好,而是在须要的时候建立类,这是就能够经过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须要和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注解