Guice 依賴繫結

alfred_zhong發表於2017-09-19

連線繫結(Linked Bingdings)

連線繫結是 Guice 最基本的一種繫結方式。這種繫結方式我們需要在自己定義的 Moduleconfigure() 中編寫繫結。如下所示:

public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Animal.class).to(Cat.class);
  }
}複製程式碼

現在當呼叫 injector.getInstance(Animal.class) 時,就會返回一個 Cat 物件。

連線繫結也可以組成鏈式,如下:

public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Animal.class).to(Cat.class);
    bind(Cat.class).to(PersianCat.class);
  }
}複製程式碼

註解繫結(Binding Annotations)

某些時候我們的一個介面可能有很多的實現,而此時我們在程式碼的某個地方想讓這個介面繫結其中的某體格實現,這個時候直接使用連線繫結就不行了。

此時我們可以使用註解(Annotation)來繫結,我們可以使用自己定義的註解,舉例:

@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface Persian {}複製程式碼

這個註解中需要主要的是 @BindingAnnotation 這個地方,這是由 Guice 提供的,來標識這個註解就是用來標識繫結的,我們也可以稱其為繫結註解(Binging Annotation)。當一個物件被多個繫結註解所標識時,Guice 就會報錯

@Inject
@Persian
private Cat cat;複製程式碼

我們仍然需要指定繫結關係,如下:

bind(Cat.class).annotatedWith(Persian.class).to(PersianCat.class);複製程式碼

其實,如果沒有什麼特殊需求的話,Guice 已經為我們提供了一種預設的註解來輔助我們進行物件繫結,@Named。仍然以上述例子來說明:

@Inject
@Named("persian")
private Cat cat;複製程式碼

此時繫結關係可以這樣寫:

bind(Cat.class)
    .annotatedWith(Names.named("persian"))
    .to(PersianCat.class);複製程式碼

例項繫結(Instance Bindings)

顧名思義,直接使用例項而來進行繫結。通常這種繫結作用於沒有什麼依賴和實現的物件上。

最普遍的應用大概就是一些引數變數的繫結了,直接 Copy 官方文件的程式碼:

bind(String.class)
    .annotatedWith(Names.named("JDBC URL"))
    .toInstance("jdbc:mysql://localhost/pizza");
bind(Integer.class)
    .annotatedWith(Names.named("login timeout seconds"))
    .toInstance(10);複製程式碼

如果例項過於複雜,就不要使用這樣的方式繫結,因為這些程式碼寫在 Moduleconfigure() 中,會拖慢程式的啟動。稍後會說到,可以使用 @Provides 來代替。

@Provides 方法

當需要某種型別的物件時,就可以使用 @Provides 方法。這類方法必須定義在 Module 中,且用 @Provides 標識,如下:

public class AppModule extends AbstractModule {
    protected void configure() {}

    @Provides
    PersianCat providePersianCat() {
        PersianCat persianCat = new PersianCat();
        persianCat.setName("Foo");
        return persianCat;
    }
}複製程式碼

這時當注入 PersianCat 型別物件時,就會從 providePersianCat() 方法中生成。

@Provides 方法也可以新增註解,這裡就直接貼官方程式碼了,不做贅述:

@Provides @PayPal
CreditCardProcessor providePayPalCreditCardProcessor(@Named("PayPal API key") String apiKey) {
    PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor();
    processor.setApiKey(apiKey);
    return processor;
}複製程式碼

Provider 繫結(Provider Bindings)

當使用了太多 @Provides 方法繫結之後,Module 就會顯得臃腫不堪。這時可以試著將這些方法從 Module 中剝離出來,只要實現 Guice 提供的 Provider 的介面即可。

public interface Provider<T> {
    T get();
}複製程式碼

Provider 介面只有一個 get() 方法,其實很簡單。直接把上述程式碼移至此處。

public class PersianCatProvider implements Provider<PersianCat> {

    public PersianCat get() {
        PersianCat persianCat = new PersianCat();
        persianCat.setName("Foo");
        return persianCat;
    }
}複製程式碼

最後如果需要,可以指定型別繫結到 Provider:

bind(Cat.class).toProvider(PersianCatProvider.class);複製程式碼

構造方法繫結(Constructor Bindings)

有時候當繫結物件有多個構造方法時,我們可能需要指定某一個構造方法,這時可以使用構造方法繫結來達到目的。如下:

try {
    bind(PersianCat.class).toConstructor(PersianCat.class.getConstructor(String.class));
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}複製程式碼

需要注意的是,這種繫結方式需要捕獲異常。

@ImplementedBy 與 @ProvidedBy

@ImplementedBy 註解用於簡化繫結配置,通常用於指定預設的實現型別。最常用的場景在於編寫 Dao 或者 Service 時,指定 Interface 的實現類。直接給出官方示例:

@ImplementedBy(PayPalCreditCardProcessor.class)
public interface CreditCardProcessor {
    ChargeResult charge(String amount, CreditCard creditCard) throws UnreachableException;
}複製程式碼

等價於:

bind(CreditCardProcessor.class).to(PayPalCreditCardProcessor.class);複製程式碼

@ProvidedBy 也是顧名思義:

@ProvidedBy(DatabaseTransactionLogProvider.class)
public interface TransactionLog {
    void logConnectException(UnreachableException e);
    void logChargeResult(ChargeResult result);
}複製程式碼

等價於

bind(TransactionLog.class).toProvider(DatabaseTransactionLogProvider.class);複製程式碼

相關文章