依賴注入

依賴

如果 Class A 中有 Class B 的一個實例,那么可以說 Class A 對 Class B 有一個依賴。比如下面類 Home 中用到一個 Father 對象,我們就說 Home 對 Father 有一個依賴。

public class Home {
    ...
    Father father;
    ...
    public Home() {
        father = new Father();
    }
}

這段代碼存在的問題:
1、如果現(xiàn)在要修改Father對象的生成方式,如需要通過new Father(String Name)來實例化Father,那就也要修改Home的代碼;
2、想測試不同F(xiàn)ather對象對Home的影響很困難,因為father的初始化被寫死在Human的構(gòu)造函數(shù)中;
3、如果new Father()過程非常緩慢,單測時我們希望用已經(jīng)初始化好的 father 對象 Mock 掉這個過程也很困難。

這種在一個類中直接創(chuàng)建另一個類的對象的代碼,被稱為硬初始化(Hard init),和硬編碼(hard-coded strings)以及硬編碼的數(shù)字(magic numbers)一樣,是一種導(dǎo)致耦合的壞味道。

依賴注入

public class Home {
    ...
    Father father;
    ...
    public Home(Father father) {
        this.father = father;
    }
}

像上面這種不是自己主動初始化依賴,而通過外部傳來依賴的方式,就稱為依賴注入。

下面是幾種實現(xiàn)依賴注入的方式:

構(gòu)造函數(shù)注入

public class Home {
    ...
    Father father;
    ...
    public Home(Father father) {
        this.father = father;
    }
}

這應(yīng)該是最簡單的依賴注入方式。Father對象在Home類之外創(chuàng)建,再傳進來。

setter注入

public class Home {
    ...
    Father father;
    ...
    public void setFather(Father father) {
        this.father = father;
    }
}

接口注入

創(chuàng)建一個接口

public interface InjectFather {
    void injectFather(Father father);
}

實現(xiàn)這個接口

class Home implements InjectFather {
    ...
    public void InjectFather (Father father) {
      this.father = father;
    }
    ...
}

在Java中實現(xiàn)依賴注入

在Java中實現(xiàn)依賴注入除了以上說的幾種,還有一種最常用的是使用注解。

public class Home {
    ...
    @Inject 
    Father father;
    ...
    public Home() {
    }

這種方式需要使用依賴注入框架,并進行簡單的配置?,F(xiàn)在 Java 語言中較流行的依賴注入框架有 Google Guice、Spring 等,而在 Android 上比較流行的有 RoboGuice、Dagger 等。

Dagger

Dagger是為Android和Java平臺提供的一個完全靜態(tài)的,在編譯時進行依賴注入的框架,原來是由Square公司維護,現(xiàn)在由Google維護。 Dagger就是用來創(chuàng)造一個容器,所有需要被依賴的對象在Dagger的容器中實例化,并通過Dagger注入到合適的地方。

Dagger的基本使用

通常情況下引用一個類的做法
先定義一個簡單的類

public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在另一個類中對其引用

public class Activity{

    private void initData() {
        User user = new User();
        user.setName("測試");
     }
}

Dagger2的做法
首先在被依賴類的構(gòu)造函數(shù)用@Inject標注

public class User {
    private String name;

    @Inject
    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

創(chuàng)建一個@Component標注的注入器接口,并在注入器中使用 void inject(MainActivity MainActivity);來表明哪里要用到注入器

@Component
public interface ActivityComponent {
 
     void inject(MainActivity MainActivity);
}

在MainActivity中對注入器進行初始化DaggerActivityComponent.builder().build().inject(this); 初始化后該注入器就可以正常使用了;在MainActivity中對需要注入的類 User用@Inject進行標注,表示該類需要被注入,即實例化

public class Activity{

    @Inject
    User user;

    private void initData() {
        //DaggerActivityComponent是注入器是在編譯的過程中生成的
        DaggerActivityComponent.builder().build().inject(this);
        user.setName("測試");
     }
}
  • @Inject 主要有兩個作用,一個是使用在構(gòu)造函數(shù)上,通過標記構(gòu)造函數(shù)讓Dagger2來使用從而提供依賴,另一個作用就是標記在需要依賴的變量讓Dagger2為其提供依賴。
  • @Component 一般用來標注接口,被標注了Component的接口在編譯時會產(chǎn)生相應(yīng)的類的實例來作為提供依賴方和需要依賴方之間的橋梁,把相關(guān)依賴注入到其中。

@Module和@Provide

@Inject是對類的構(gòu)造函數(shù)進行標注來進行實例化的,但是有些類,比如第三方OkHttpClient,我們是無法對其源碼進行修改的即對其構(gòu)造函數(shù)進行標注,這個時候我們就用到了@Module。

@Module是和@Component配合使用的,意思就是告訴注入器,如果你在實例化對象的時候,沒有找到合適的構(gòu)造函數(shù),你就來我這里找,@Module通常標注一個類,該類里面可以實例化各種類,Component在注入對象的時候先去Module中找,如果找不到就會檢查所有被@Inject標注的構(gòu)造函數(shù)

@Module
public class ActivityMoudle {

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient() {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        Interceptor apikey = chain -> chain.proceed(chain.request().newBuilder()
                .addHeader("apikey", Constants.Api_Key).build());

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(Constants.HTTP_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .connectTimeout(Constants.HTTP_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .addInterceptor(apikey)
                .addInterceptor(loggingInterceptor)
                .build();

        return okHttpClient;
    }

}
  • @Provide 用來標注一個方法,告訴注入器,我標注的方法你可以用來提供實例;

修改注入器

@Singleton
@Component(modules = ActivityMoudle.class)
public interface ActivityComponent {

    void inject(MainActivity MainActivity);
}

Dagger原理淺析

我們先新建三個類,這三個類的內(nèi)容和上面的一致:

編譯之后生成了對應(yīng)的三個類:

我們看DaggerActivityComponent的內(nèi)容,其實就是實現(xiàn)了我們定義的ActivityComponent接口:

package com.hochan.myapplication;

import dagger.MembersInjector;
import dagger.internal.MembersInjectors;
import javax.annotation.Generated;

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent implements ActivityComponent {
  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

  public static ActivityComponent create() {  
    return builder().build();
  }

  private void initialize(final Builder builder) {  
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), User_Factory.create());
  }

  @Override
  public void myInject(MainActivity activity) {  
    mainActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private Builder() {  
    }
  
    public ActivityComponent build() {  
      return new DaggerActivityComponent(this);
    }
  }
}

可以看到我們在接口中定義的myInject()方法:

  @Override
  public void myInject(MainActivity activity) {  
    mainActivityMembersInjector.injectMembers(activity);
  }

就是在這個方法中把變量注入到Mainactivity中。

再看mainActivityMembersInjector這個變量,其實就是一個MembersInjector接口,其初始化在initialize()中完成:

  private void initialize(final Builder builder) {  
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), User_Factory.create());
  }

而MainActivity_MembersInjector就是生成的另外一個類,這個類就是一個注入器。有多少個類依賴了其他的類,就有多少個這樣的注入器類生成。在這個類的injectMembers()方法中完成了對象的注入:

  @Override
  public void injectMembers(MainActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.mUser = mUserProvider.get();
  }

還有一個編譯之后生成的類User_Factory,就是一個對象的提供者。

@Generated("dagger.internal.codegen.ComponentProcessor")
public enum User_Factory implements Factory<User> {
INSTANCE;

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

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

這里User的構(gòu)造函數(shù)是沒有參數(shù)的,我們把它改成有參數(shù)再編譯看會變成什么:

    @Inject
    public User(String name){
    }

編譯之后報錯,User_Factory多了一個參數(shù)nameProvider:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class User_Factory implements Factory<User> {
  private final Provider<String> nameProvider;

  public User_Factory(Provider<String> nameProvider) {  
    assert nameProvider != null;
    this.nameProvider = nameProvider;
  }

  @Override
  public User get() {  
    return new User(nameProvider.get());
  }

  public static Factory<User> create(Provider<String> nameProvider) {  
    return new User_Factory(nameProvider);
  }
}

get()函數(shù)變成了:

  @Override
  public User get() {  
    return new User(nameProvider.get());
  }

而在DaggerActivityComponent中的初始化并沒有把這個參數(shù)傳進來:

從上面的介紹我們可以知道,可以通過module來讓Dagger找到獲取實例的方法。這里我們新建一個module來讓Dagger獲得初始化User要用到的name字符串:

@Module
public class ActivityModule {

    @Provides
    String myName(){
        return "names";
    }
}

然后在ActivityComponent中聲明這個類:

@Component(modules = ActivityModule.class)
public interface ActivityComponent {

    void myInject(MainActivity activity);
}

之后就運行通過了,我們會發(fā)現(xiàn)編譯之后又多了一個類:ActivityModule_MyNameFactory

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ActivityModule_MyNameFactory implements Factory<String> {
  private final ActivityModule module;

  public ActivityModule_MyNameFactory(ActivityModule module) {  
    assert module != null;
    this.module = module;
  }

  @Override
  public String get() {  
    String provided = module.myName();
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<String> create(ActivityModule module) {  
    return new ActivityModule_MyNameFactory(module);
  }
}

在get()方法里通過ActivityModule調(diào)用定義好的方法來獲取到name的值。這里定位那個函數(shù)是獲取name的函數(shù)應(yīng)該是通過返回的類型來判斷的,因為如果在ActivityModule中有多個返回String的函數(shù)就會報錯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容