Dagger2 小结

Dagger2是Google在Square开源的Dagger的基础上将反射注入的方式修改到编译时而得到的依赖注入框架。
官方地址:GitHub
官方文档:https://google.github.io/dagger/

四个关键注解:

@Inject

用于类构造器和类的属性。

  • 用在类的构造器上用来注解提供对象的方法,Dagger会根据该构造器来生成该类的工厂类ClassName_Factory。工厂类实现了Factory<T>接口,通过覆写的get()方法调用注解的构造方法提供类的对象:
public final class Noodle_Factory implements Factory<Noodle> {
  private static final Noodle_Factory INSTANCE = new Noodle_Factory();

  @Override
  public Noodle get() {
    return new Noodle();
  }

  public static Factory<Noodle> create() {
    return INSTANCE;
  }

  /** Proxies {@link Noodle#Noodle()}. */
  public static Noodle newNoodle() {
    return new Noodle();
  }
}

原始类:

class Noodle {

    @Inject
    public Noodle() {
    }

    @Override
    public String toString() {
        return "面条";
    }
}
  • 用在类的属性上用来注解该属性需要依赖注入。Dagger会为根据该类需要注入的属性生成ClassName_MembersInjector注入类,该类实现了MembersInjector<T>接口。通过静态的create(arg...)方法来生成实例,该方法的参数类型为需要注入的属性对应的Provider<T>类型(Factory<T>Provider<T>的子类型),参数个数与需要注入的字段个数一致。注入类通过覆写的injectMembers(T instance)方法来注入目标类需要的属性:
public final class ZhaiNan_MembersInjector implements MembersInjector<ZhaiNan> {
  private final Provider<Baozi> baoziProvider;

  private final Provider<Noodle> noodleProvider;

  public ZhaiNan_MembersInjector(Provider<Baozi> baoziProvider, Provider<Noodle> noodleProvider) {
    assert baoziProvider != null;
    this.baoziProvider = baoziProvider;
    assert noodleProvider != null;
    this.noodleProvider = noodleProvider;
  }

  public static MembersInjector<ZhaiNan> create(
      Provider<Baozi> baoziProvider, Provider<Noodle> noodleProvider) {
    return new ZhaiNan_MembersInjector(baoziProvider, noodleProvider);
  }

  @Override
  public void injectMembers(ZhaiNan instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.baozi = baoziProvider.get();
    instance.noodle = noodleProvider.get();
  }

  public static void injectBaozi(ZhaiNan instance, Provider<Baozi> baoziProvider) {
    instance.baozi = baoziProvider.get();
  }

  public static void injectNoodle(ZhaiNan instance, Provider<Noodle> noodleProvider) {
    instance.noodle = noodleProvider.get();
  }
}

若一个类自身需要为别的类提供依赖(他的构造器用@Inject注解),同时也需要别的类提供依赖(他的属性用@Inject注解),他对应的工厂类会依赖于他对应的MembersInjector,并调用MembersInjectors.injectMembers()来为自身注入依赖:

public final class ZhaiNan_Factory implements Factory<ZhaiNan> {
  private final MembersInjector<ZhaiNan> zhaiNanMembersInjector;

  public ZhaiNan_Factory(MembersInjector<ZhaiNan> zhaiNanMembersInjector) {
    assert zhaiNanMembersInjector != null;
    this.zhaiNanMembersInjector = zhaiNanMembersInjector;
  }

  @Override
  public ZhaiNan get() {
    return MembersInjectors.injectMembers(zhaiNanMembersInjector, new ZhaiNan());
  }

  public static Factory<ZhaiNan> create(MembersInjector<ZhaiNan> zhaiNanMembersInjector) {
    return new ZhaiNan_Factory(zhaiNanMembersInjector);
  }
}

原始类:

public class ZhaiNan {

    @Inject
    Baozi baozi;

    @Inject
    Noodle noodle;

    @Inject
    public ZhaiNan() {
    }

    public String eat() {
        StringBuilder sb = new StringBuilder();
        sb.append("我吃的是 ");
        if (baozi != null) {
            sb.append(baozi.toString());
        }

        if (noodle != null) {
            sb.append("  ");
            sb.append(noodle.toString());
        }
        return sb.toString();
    }

}

class Noodle {

    @Inject
    public Noodle() {
    }

    @Override
    public String toString() {
        return "面条";
    }
}

@Module 和 @Provides

上面介绍了Dagger生成的核心类,即为被注入的属性生成的工厂类以及注入类,工厂类用来提供该属性需要的对象,注入类提供将该对象绑定要属性需要的方法。生成工厂类需要使用@Inject注解提供依赖的类的构造函数,但现实情况是,我们很多时候没法修改该类,这个时候我们就需要手动实现提供该该类的对象的方法。使用@Module@Provides来解决这个问题。@Module 用来注解我们自己实现的工厂类,@Provides用来注解里面的工厂方法,Dagger会为每一个工厂方法生成一个工厂类。假如@Module标注的类名为ZhaiNanModule@Provides标注的方法名为provideBaozi(),生成的工厂类则为:

public final class ZhaiNanModule_ProvideBaoziFactory implements Factory<Baozi> {
  private final ZhaiNanModule module;

  public ZhaiNanModule_ProvideBaoziFactory(ZhaiNanModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Baozi get() {
    return Preconditions.checkNotNull(
        module.provideBaozi(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Baozi> create(ZhaiNanModule module) {
    return new ZhaiNanModule_ProvideBaoziFactory(module);
  }
}

Module 类:

@Module
public class ZhaiNanModule {

    @Provides
    public ZhaiNan provideZaiNan() {
        return new ZhaiNan();
    }

    @Provides
    public Noodle provideNoodle() {
        return new Noodle();
    }

    @Provides
    public Baozi provideBaozi() {
        return new Baozi();
    }
}

注意:

  • @Provides标注的方法中直接new 出来的对象,即使该对象内部有需要被注入的属性,Dagger也没法自动去完成依赖注入。即Dagger不会为new出来的对象自动注入依赖。
  • 若提供依赖的类的构造方法使用了@Inject注解,并且@Module注解的类也中提供了返回该类的方法,Dagger会优先使用@Module注解的类中的方法。

@Component()

依赖提供方和依赖需求方的纽带。@Component()注解的接口或抽象类中定义提供已经注入好依赖的对象的方法(前提是Module中或Dagger生成的代码中提供该对象的方法会注入该对象的依赖)或者为对象注入依赖方法。被它注解的接口中定义的方法若为返回一个对象,则该方法执行完后返回对象中定义的所有需要被注入的依赖都会被注入好,若该方法接受一个对象作为参数,则该方法执行完后该对象内需要被注入的属性都会被注入好。
若接口名称为ZhaiNanComponent,则Dagger生成的类名为DaggerZhaiNanComponent,并且实现了接口中定义的方法:
原始类:

@Component(modules = {ZhaiNanModule.class}) // 指定提供依赖方法的模块
public interface ZhaiNanComponent {
    ZhaiNan get();

    void inject(MainActivity mainActivity);

    void inject(ZhaiNan zhaiNan);
}

生成的类:

public final class DaggerZhaiNanComponent implements ZhaiNanComponent {
  private Provider<ZhaiNan> provideZaiNanProvider;

  private MembersInjector<MainActivity> mainActivityMembersInjector;

  private Provider<Baozi> provideBaoziProvider;

  private Provider<Noodle> provideNoodleProvider;

  private MembersInjector<ZhaiNan> zhaiNanMembersInjector;

  private DaggerZhaiNanComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static ZhaiNanComponent create() {
    return new Builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideZaiNanProvider = ZhaiNanModule_ProvideZaiNanFactory.create(builder.zhaiNanModule);

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideZaiNanProvider);

    this.provideBaoziProvider = ZhaiNanModule_ProvideBaoziFactory.create(builder.zhaiNanModule);

    this.provideNoodleProvider = ZhaiNanModule_ProvideNoodleFactory.create(builder.zhaiNanModule);

    this.zhaiNanMembersInjector =
        ZhaiNan_MembersInjector.create(provideBaoziProvider, provideNoodleProvider);
  }

  @Override
  public ZhaiNan get() {
    return provideZaiNanProvider.get();
  }

  @Override
  public void inject(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }

  @Override
  public void inject(ZhaiNan zhaiNan) {
    zhaiNanMembersInjector.injectMembers(zhaiNan);
  }

  public static final class Builder {
    private ZhaiNanModule zhaiNanModule;

    private Builder() {}

    public ZhaiNanComponent build() {
      if (zhaiNanModule == null) {
        this.zhaiNanModule = new ZhaiNanModule();
      }
      return new DaggerZhaiNanComponent(this);
    }

    public Builder zhaiNanModule(ZhaiNanModule zhaiNanModule) {
      this.zhaiNanModule = Preconditions.checkNotNull(zhaiNanModule);
      return this;
    }
  }
}

现在我们可以这么使用该类:

@Inject
ZhaiNan zhaiNan;

// 为对象注入依赖
ZhaiNanComponent zhaiNanComponent = DaggerZhaiNanComponent.builder()
        .zhaiNanModule(new ZhaiNanModule())
        .build();
zhaiNanComponent.inject(this); // 执行后zhaiNan就会被赋值
zhaiNanComponent.inject(zhaiNan); // 要执行这一步,因为zhaiNan在Module里是new出来的

// 获取一个依赖被注入完毕的对象
zhaiNan = DaggerZhaiNanComponent.builder().build().get();

注意:若Module中提供依赖的方法为static,则可以用ZhaiNanComponent zhaiNanComponent = DaggerZhaiNanComponent.create();来创建Component

辅助性注解

@Singleton 和 @Scope

@Singleton@Scope被注解的一个注解,用来和@Provides注解一起注解提供依赖的方法,被@Singleton注解的方法能在@Component范围内提供单例(该Component同时也需要被@Singleton注解)。我们可以直接使用@Singleton,也可以像这样自定义一个局部单例注解:

@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ActivityScoped {
}

其中@Scope是必须的,其他可选。现在我们可以使用@ActivityScoped去注解@Provides方法和@Component类,来指定在该@Component@Provides方法返回同一个对象实例。使用@ActivityScoped注解上面的provideBaozi()方法后,生成的DaggerZhaiNanComponent类的private void initialize(final Builder builder)方法中生成provideBaoziProvider方式变成了:

// 原来是
//this.provideBaoziProvider = ZhaiNanModule_ProvideBaoziFactory.create();
this.provideBaoziProvider = DoubleCheck.provider(ZhaiNanModule_ProvideBaoziFactory.create());

使用了DoubleCheck<T>类来生成了一个代理,通过DoubleCheck类覆写的get() 方法来提供单例,所以它能实现Component级别的单例。

@Named 和 @Qualifiers

当你需要在Module中定义多个返回同类型的方法的时候,@name就派上用场了。@name@Qualifiers注解的一个注解,用来注解@Provides注解的提供依赖对象的方法,和@Inject注解的需要被注入的对象,以区分使用哪个方法来提供实例:

@Module
public class ZhaiNanModule {
    @Provides
    @Named("baozi1")
    public static Baozi provideBaozi1() {
        return new Baozi();
    }

    @Provides
    @Named("baozi2")
    public static Baozi provideBaozi2() {
        return new Baozi();
    }
}

public class MainActivity extends AppCompatActivity {

    @Inject
    ZhaiNan zhaiNan;

    @Inject
    @Named("baozi1")
    Baozi baozi;

    @Inject
    @Named("baozi2")
    Baozi baozi2;
}

生成的DaggerZhaiNanComponent类的private void initialize(final Builder builder)方法中生成mainActivityMembersInjector方式变成了:

this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(
            ZhaiNanModule_ProvideZaiNanFactory.create(),
            ZhaiNanModule_ProvideBaozi1Factory.create(),
            ZhaiNanModule_ProvideBaozi2Factory.create());

若不想写每次写个name,可以自定义一个注解:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Baozi1 {
}

延迟加载和强制重新加载

分别使用Lazy<T>Provider<T>类来实现:

public class Test {

    @Inject
    @Named("Test")
    Lazy<String> name;

    @Inject
    Provider<Integer> randomValue;

    public String getName() {
        return name.get();
    }

    public int getRandomValue() {
        return randomValue.get().intValue();
    }
}

这样当第一次调用getName()时,我们需要的String对象才被创建;而每次调用getRandomValue()时,一个新的Integer对象都会被创建。

Component依赖

若一个Component需要依赖另一个,可以通过指定@Component(modules = XModule.class, dependencies = OtherComponent.class),这样就可以使用OtherComponent定义的方法。在创建Component的时候把依赖的Component传入:

XiaoChiComponent xiaoChiComponent = DaggerXiaoChiComponent.builder()
        .build();

DaggerFoodComponent.builder()
        .xiaoChiComponent(xiaoChiComponent)
        .build()
        .inject(this);

SubComponent

Component依赖类似于组合,而SubComponent类似于继承:

@Subcomponent(modules = FoodModule.class)
public interface SubComponent {
    void inject(ThirdActivity activity);
}

@Component(modules = XiaoChiModule.class)
public interface ParentComponent {
    SubComponent provideSubComponent();
}

DaggerParentComponent.builder().build()
                .provideSubComponent().inject(this);

参考

轻松学,听说你还没有搞懂 Dagger2

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容