走馬觀花-Dagger2 - @Inject 和 @Component

weixin_33806914發表於2019-02-15

@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();

相關文章