走马观花-Dagger2 - @Inject 和 @Component

@Inject

  • @Inject 使用在一个类的属性上,表示该类需要一个依赖
  • @Inject 使用在一个类的构造方法上,表示由该构造方法提供依赖

假设有个路人Passerby,需要一辆车Car,则可以表示为 Passerby 依赖于 Car。代码表示:

Passerby :

public class Passerby {
    public Car mCar;
    public Passerby(Car car) {
        mCar = car;
        System.out.println("I'm a Passerby!");
    }
}

Car :

public class Car {
    @Inject
    public Car() {
        System.out.println("I'm a Car!");
    }
}

当且仅当在 Car 的构造器上使用 @Inject 时,编译,在 \build\generated\source\apt\debug\com\oliver\test 目录下自动生成了 Car_Factory.java 文件:

public final class Car_Factory implements Factory<Car> {
  private static final Car_Factory INSTANCE = new Car_Factory();

  @Override
  public Car get() {
    return provideInstance();
  }

  public static Car provideInstance() {
    return new Car();
  }

  public static Car_Factory create() {
    return INSTANCE;
  }

  public static Car newCar() {
    return new Car();
  }
}

Car_Factory 实现了 Factory<T> 接口, Factory<T> 继承至 Provider<T> 接口:

public interface Factory<T> extends Provider<T> {
}

public interface Provider<T> {
    /**
     *  返回注入的实例
     */
    T get();
}

观察 Car_Factory.java ,可以知道:

  • 构造器使用了 @Inject 注解,就会生成 类名_FactoryFactory<T> 实现类。
  • get() 确实返回我们注入的实例
  • 自动生成两个返回 new Car() 的方法,分别是 newCar()provideInstance()

Passerby 的依赖标注 @Inject 注解,再次编译:

 @Inject
 public Car mCar;

\build\generated\source\apt\debug\com\oliver\test 目录下多了一个 Passerby_MembersInjector 的类:

public final class Passerby_MembersInjector implements MembersInjector<Passerby> {
  private final Provider<Car> mCarProvider;

  public Passerby_MembersInjector(Provider<Car> mCarProvider) {
    this.mCarProvider = mCarProvider;
  }

  public static MembersInjector<Passerby> create(Provider<Car> mCarProvider) {
    return new Passerby_MembersInjector(mCarProvider);
  }

  @Override
  public void injectMembers(Passerby instance) {
    injectMCar(instance, mCarProvider.get());
  }

  public static void injectMCar(Passerby instance, Car mCar) {
    instance.mCar = mCar;
  }
}

该类实现了 MembersInjector<T> 接口:

public interface MembersInjector<T> {
  void injectMembers(T instance);
}

注意 Passerby_MembersInjector 的方法 injectMembers(T instance),其参数是 Passerby 类型,也就是说,该方法的使命就是给其参数持有的成员变量赋值,然后外界想办法获得这个参数就行了,参数依赖的对象已经在此处完成了注入的过程。

 @Override
  public void injectMembers(Passerby instance) {
    injectMCar(instance, mCarProvider.get());
  }

  public static void injectMCar(Passerby instance, Car mCar) {
    instance.mCar = mCar;
  }

另外, Passerby_MembersInjector 多出了一个成员 Provider<Car>,即一个类的成员变量标注有 @Inject 注解,那么这个成员就会在生成类中生成相应的 Provider<T> 成员。根据上面对 Car_Factory 的分析,大胆猜测,其实这里的 Provider<Car> 就是生成的 Car_Factory 。所需依赖也正好在此处注入,即:

instance.mCar =  mCarProvider.get();
mCar = new Car();


///////////////////////////// Car_Factory /////////////////////////////
@Override
public Car get() {
    return provideInstance();
}

public static Car provideInstance() {
    return new Car();
}

上面说到,我们猜测 Provider<Car> 就是生成的 Car_Factory 。那 Provider<Car> 是怎样赋值的呢?

public Passerby_MembersInjector(Provider<Car> mCarProvider) {
    this.mCarProvider = mCarProvider;
  }

  public static MembersInjector<Passerby> create(
      Provider<Car> mCarProvider) {
    return new Passerby_MembersInjector(mCarProvider);
  }

可以看到,主要是通过 Passerby_MembersInjector.create()new Passerby_MembersInjector()赋值,其中 create() 又是直接调用构造方法的。那这两个方法是调用了哪一个?谁调用的?这就要说到另外一个注解了: @Component

Dagger 的主要功能时依赖注入,从而达到解耦的目的。所以不会在Xxx_MembersInjector 中直接赋值,这样的话和 mCar = new Car() 没什么区别?所以我猜测,引入了 @Component 是为了达到依赖方和被依赖方不直接耦合的目的。但是,这样就造成了依赖方和被依赖方都与 @Component 耦合了。所以其实我也不太清楚 @Component 的具体作用

2019-02-12
@Component 限定了提供依赖的 Module 类,如果有过多个 Module 都提供该依赖,@Componentmodules 字段就指定了到底是哪个具体 Module 来完成这个依赖的提供。

@Component

@Inject 使用在成员上表示需要依赖对象,使用在构造器上表示提供自身对象。两者之间需要 @Component 注解作为依赖关系形成的桥梁。

@Component 使用在接口或抽象类上,编译后在 \build\generated\source\apt\debug\com\oliver\test 目录下生成一个名称为 Dagger + 被注解的类名称 的类。例如:

@Component
public interface Ofo {}

生成类名称: DaggerOfo

@Component 必须包含至少一个abstract component method,以下简称 CMCM 可以随意命名,但是必须有满足 Provider 或者 MembesInjector 约束的签名。

Provider Method :

Provider Method,以下简称 PM ,没有参数但是有返回值。返回一个 {@link Inject injected}{@link Provides provided} 类型,方法还可以被 @Qualifier 注解标注。也就是说,返回值必须是 被注入类型被提供类型

  • 被注入类型 表示其构造器被 @Inject 标注
  • 被提供类型 表示 @Component 包含的 @Module 中被 @Providers 注解标注的方法的返回值类型

注解 @Module@Providers 是什么?下面会说到。

MembersInjector Method :

MembersInjector Method ,以下简称 MMMM 有一个参数,并将依赖项注入每个 {@link Inject} 注解的字段和传递实例的方法,MM 返回 void, 为方便链式调用,也可返回 参数类型,。也就是说,参数类中有成员变量被 @Inject 标注,且该成员变量类的构造器也被 @Inject 标注。这样的话,当 @Compment 实现类【DaggerOfo】重写该方法【void inject(MainActivity activity)】时,就会通过成员变量相应生成的【Passerby_Factory】构造【Passerby】实例,赋值给参数【MainActivity】的成员变量【mPasserby】。具体如下:

public class Passerby {
    @Inject
    public Car mCar;

    public Passerby(){}

    @Inject
    public Passerby(Car car) {
        mCar = car;
        System.out.println("I'm a Passerby!");
    }
    public void go(){
        mCar.go();
    }
}

MainActivity 中使用:

public class MainActivity extends AppCompatActivity {

    @Inject
    Passerby mPasserby;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 注意,此处注入的参数this不能为null,
        DaggerOfo.builder().build().inject(this);
        mPasserby.go();
    }
}

开始使用 @Component 注解:

@Component
public interface Ofo {
    
//    Passerby getPasserby();
    
    // 使用 MembersInjector Method
    void inject(MainActivity activity);
}

在标注了 @Component 注解的类中使用 MM,生成类如下:

public final class DaggerOfo implements Ofo {
  private DaggerOfo(Builder builder) {}

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

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

  private Passerby getPasserby() {
    return injectPasserby(Passerby_Factory.newPasserby(new Car()));
  }

  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }

  private Passerby injectPasserby(Passerby instance) {
    Passerby_MembersInjector.injectMCar(instance, new Car());
    return instance;
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectMPasserby(instance, getPasserby());
    return instance;
  }

  public static final class Builder {
    private Builder() {}

    public Ofo build() {
      return new DaggerOfo(this);
    }
  }
}

可以看到,当我们调用 inject() 时,会调用到 Passerby_MembersInjector#injectMCar():

public static void injectMCar(Passerby instance, Car mCar) {
    // 直接赋值,假设传进来的 instance == null,报空指针就在所难免了
    instance.mCar = mCar;
}

调用 injectMainActivity(MainActivity) 之后,在层层调用,就达到了给成员变量 mPasserbyPasserby 的成员变量 mCar 的注入。

现在在 @Component 注解的类中使用 MP:

@Component
public interface Ofo {

    Passerby getPasserby();

//    void inject(Passerby passerby);
}

生成类 DaggerOfo 如下:

public final class DaggerOfo implements Ofo {
    private DaggerOfo(Builder builder) {}

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

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

    @Override
    public Passerby getPasserby() {
        return injectPasserby(Passerby_Factory.newPasserby(new Car()));
    }

    private Passerby injectPasserby(Passerby instance) {
        Passerby_MembersInjector.injectMCar(instance, new Car());
        return instance;
    }

    public static final class Builder {
        private Builder() {}

        public Ofo build() {
            return new DaggerOfo(this);
        }
    }
}

可以看到,调用 getPasserby() 时会自动生成一个 Passerby 实例,然后给其注入依赖。外界可通过返回值获取到该实例。

总结

  • @Inject 使用在一个类的属性上,表示该类需要一个依赖;使用在一个类的构造方法上,表示由该构造方法提供依赖
  • @Component 注解作为依赖关系形成的桥梁,至少包含一个 CM。编译之后会生成 DaggerXxx
  • CM类型是 MM ,在类【假设为A】中使用 DaggerXxx ,则 A 中必须有 @Inject 注解标注的成员变量,用于赋值。因为经过上面生成的 DaggerOfo 来说,在 injectXxx() 中都是这样模板代码:instance.mXxx = Xxx_Factory.newXxx(),即赋值操作。
  • CM类型是 PM , 在类【假设为A】中使用 DaggerXxx 来获取需要注入的对象,然后在使用;例如:
Passerby mPasserby = DaggerOfo.builder().build().getPasserby();
mPasserby.go();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,585评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,923评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,314评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,346评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,718评论 3 291
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,828评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,020评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,758评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,486评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,722评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,196评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,546评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,211评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,132评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,916评论 0 200
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,904评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,758评论 2 274

推荐阅读更多精彩内容