and: dagger 抄襲1

ref

Dagger 2 完全解析(一),Dagger 2 的基本使用與原理

https://www.jianshu.com/p/26d9f99ea3bb

notes

什麼是依賴注入

依賴注入(Dependency Injection,簡稱 DI)是用於實現控制反轉(Inversion of Control,縮寫爲 IoC)最常見的方式之一,控制反轉是面向對象編程中的一種設計原則,用以降低計算機代碼之間耦合度。控制反轉的基本思想是:藉助“第三方”實現具有依賴關係的對象之間的解耦。一開始是對象 A 對 對象 B 有個依賴,對象 A 主動地創建 對象 B,對象 A 有主動控制權,實現了 Ioc 後,對象 A 依賴於 Ioc 容器,對象 A 被動地接受容器提供的對象 B 實例,由主動變爲被動,因此稱爲控制反轉。注意,控制反轉不等同於依賴注入,控制反轉還有一種實現方式叫“依賴查找”(Denpendency Lookup)。更多控制反轉的信息請看控制反轉的維基百科。

在這裏插入圖片描述

依賴注入就是將對象實例傳入到一個對象中去(Denpendency injection means giving an object its instance variables)。依賴注入是一種設計模式,降低了依賴和被依賴對象之間的耦合,方便擴展和單元測試。

依賴注入的實現方式

你已經不知覺地使用了依賴注入

基於構造函數,在構造對象時注入所依賴的對象。

public class Man {
    Car car;
    public Man(Car car) {
        this.car = car;
    }
    ...
}

基於 set 方法,使用 setter 方法來讓外部容器調用傳入所依賴的對象。

public class Man {
    ...
    public void setCar(Car car) {
        this.car = car;
    }
}

基於接口,使用接口來提供 setter 方法。

public interface CarInjector {
    void injectCar(Car car);
}

public class Man implements CarInjector {
    ...
    @Override
    public void injectCar(Car car) {
        this.car = car;
    }
}

基於註解,Dagger 2 依賴注入框架就是使用 @Inject 完成注入。

public class Man {
    @Inject
    Car car;
    ...
}

下面是 Dagger 2 的一些資源地址:

Github:https://github.com/google/dagger

官方文檔:https://google.github.io/dagger//

API:http://google.github.io/dagger/api/latest/


public final class Man_MembersInjector implements MembersInjector<Man> {
  private final Provider<Car> carProvider;

  public Man_MembersInjector(Provider<Car> carProvider) {
    assert carProvider != null;
    this.carProvider = carProvider;
  }

  public static MembersInjector<Man> create(Provider<Car> carProvider) {
    return new Man_MembersInjector(carProvider);
  }

  @Override
  public void injectMembers(Man instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.car = carProvider.get();
  }

  public static void injectCar(Man instance, Provider<Car> carProvider) {
    instance.car = carProvider.get();
  }
}

從上面的injectMembers方法中可以看到注入依賴的代碼是instance.car = carProvider.get();,所以@Inject標註的成員屬性不能是private的,不然無法注入。

上面完成兩步,通過 Dagger 2 生成的代碼代碼可以知道,生成了 Man 的成員屬性注入類和 Car 的工廠類,接下來需要的就是新建工廠實例並調用成員屬性注入類完成 car 的實例注入。完成這個過程的橋樑就是dagger.Component。

Component 橋樑

@Component可以標註接口或抽象類,Component 橋樑可以完成依賴注入過程,其中最重要的是定義注入接口,調用注入接口就可以完成 Man 所需依賴的注入。
在這裏插入圖片描述

@Component
public interface ManComponent {

    void injectMan(Man man);  // 注入 man 所需要的依賴

}

build 後會生成帶有Dagger前綴的實現該接口的類: DaggerManComponent

public final class DaggerManComponent implements ManComponent {
  private MembersInjector<Man> ManMembersInjector;

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

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

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

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

    this.ManMembersInjector = Man_MembersInjector.create(Car_Factory.create());    // 找到 Man 的成員屬性注入類,創建依賴的工廠
  }

  @Override
  public void injectMan(Man man) {
    ManMembersInjector.injectMembers(man);  // 完成依賴注入
  }

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

    public ManComponent build() {
      return new DaggerManComponent(this);
    }
  }
}

todo 學習複習建造者 模式

從上面生成的代碼可以看出來 Component 就是連接依賴的對象實例和需要注入的實例屬性之間的 橋樑Component 會查找目標類對應的成員屬性注入類(即 MembersInjector),然後把依賴屬性的工廠實例(即 Car_Factory.create())傳給注入類,再使用 Component 一開始定義的接口就能完成依賴注入。

注意,Component 中注入接口的參數必須爲 需要注入依賴的類型 ,不能是 Man 的父類或子類, 必須就是他這個類本身,注入接口返回值爲 void,接口名可以任意。

接下來只需要在 Man 中調用injectMan方法就能完成注入。

public class Man {
    ...
    public Man() {
        DaggerManComponent.create().injectMan(this);
    }
    ...
}

Module

使用@Inject標註構造函數來提供依賴的對象實例的方法,不是萬能的,在以下幾種場景中無法使用:

接口沒有構造函數

第三方庫的類不能被標註

構造函數中的參數必須配置

這時,就可以用 @Provides 標註的方法來提供依賴實例,方法的返回值就是依賴的對象實例, @Provides 方法必須在 Module 中, Module 即用 @Module 標註的類。所以 Module 是提供依賴的對象實例的另一種方式。

@Module
public class CarModule {
    @Provides
    static Car provideCar() {
        return new Car();
    }
}

約定俗成的是@Provides方法一般以provide爲前綴,Moudle 類以Module爲後綴,一個 Module 類中可以有多個@Provides方法。

接下來,需要把可以提供依賴實例的 Module 告訴 Component:

@Component(modules = CarModule.class)
public interface ManComponent {

    void injectMan(Man man);  // 注入 man 所需要的依賴

}

build之後,Module 和 Component 生成的類爲:

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

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

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

  /** Proxies {@link CarModule#provideCar()}. */
  public static Car proxyProvideCar() {
    return CarModule.provideCar();
  }
}

CarModule_ProvideCarFactory 和之前的 Car_Factory 類似,都實現 Factory 接口。

而生成的 DaggerManComponent 和之前相比只改變了一個方法:

private void initialize(final Builder builder) {
    this.manMembersInjector = Man_MembersInjector.create(CarModule_ProvideCarFactory.create());
}

aka: 只是提供依賴實例的工廠變爲了 CarModule 對應的工廠。

總結

現在再來看 Dagger 2 最核心的三個部分:

在這裏插入圖片描述
下面再講述上面提到的在 Dagger 2 種幾個註解的用法:

@Inject 一般情況下是標註成員屬性和構造函數,標註的成員屬性不能是private,Dagger 2 還支持方法注入,@Inject還可以標註方法。
@Provides 只能標註方法,必須在 Module 中。
@Module 用來標註 Module 類
@Component 只能標註接口或抽象類,聲明的注入接口的參數類型必須和目標類一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章