1. 程式人生 > >Guice源碼學習(一)基本原理

Guice源碼學習(一)基本原理

val args 就會 figure 但是 imp 屬性 div develop

Guice是Google開發的一個開源輕量級的依賴註入框架,運行速度快,使用簡單。

項目地址:https://github.com/google/guice/

最新的版本是4.1,本文基於此版本。

Guice的使用方法請參見我的前篇博文:《Guice 4.1教程》

0. Guice的使用範例

先分析最簡單的例子

public interface Dog {
    void bark();
}

public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println(
"i am black dog"); } } public class GuiceTest { public static void main(String[] args) { Injector injector = Guice.createInjector(new Module() { @Override public void configure(Binder binder) { binder.bind(Dog.class).to(BlackDog.class); } }); injector.getInstance(Dog.
class).bark(); } }

從上面的代碼中,我們可以清楚的看出,Guice先利用自定義的Module創建了一個Injector,然後調用這個Injector的getInstance方法獲得了Dog接口的BlackDog子類的一個實例

Dog接口與BlackDog子類的的綁定,則是在Module.configure方法中設置的。

1. Guice的本質

在分析源碼之間,我們不妨先考慮一下Guice的本質。

Q:Guice主要解決的是什麽問題?

A:依賴註入的問題

Q:怎麽知道哪裏的依賴會被註入?

A:[email protected]/構造方法/Setter方法會觸發註入

Q:Guice怎麽知道該註入什麽實例?

A:初始化Injector時傳入的Module對象,[email protected],以及屬性的註解都會限定被註入的對象的類型,如果不能限定到某種唯一特定的類型,Guice會拋出錯誤

這樣我們就有一個大概的猜想了,Guice內部肯定維護了從接口到實現類的關系

其形式應該類似於一個Key為接口的class,value為實現類的Map

每次應用調用getInstance想要獲取某個接口的實現類的實例的時候,就會去這個Map裏檢索,找到對應的實現類後調用其構造方法並返回

如果實現類中又有需要被註入的屬性,則遞歸調用這一註入過程即可。

實際上,Guice內部確實維護了接口到實現類(也可能是Provider或者某個實例對象)的映射。

但是考慮到泛型與註解,Guice不是直接用接口的class作為key,而是用com.google.inject.Key來描述某個可以被註入的接口的,com.google.inject.Key的定義如下所示

public class Key<T> {

  private final AnnotationStrategy annotationStrategy;//註解限定

  private final TypeLiteral<T> typeLiteral;//類型限定
  private final int hashCode;
  private String toString;
}

Key內部有兩個關鍵屬性,annotationStrategy與typeLiteral,分別表示這個接口的註解限定與類型限定。

以本文開頭部分的場景為例,Dog接口對應的Key中,annotationStrategy就是NullAnnotationStrategy(無註解),typeLiteral則為com.cc.test.guice.Dog(Dog接口的全稱)

在createInjector函數中,Guice會完成從Dog接口到BlackDog實現類的綁定

而在getInstance方法中,傳入的Dog.class也會被組裝成Key對象,Guice很容易就可以根據根據這個Key對象找到正確的實現類BlackDog,這樣就可以調用構造方法創建對象並返回了

2. Guice.createInjector方法的調用鏈

  public static Injector createInjector(Module... modules) {
    return createInjector(Arrays.asList(modules));
  }

  public static Injector createInjector(Iterable<? extends Module> modules) {
    return createInjector(Stage.DEVELOPMENT, modules);
  }

  public static Injector createInjector(Stage stage,
      Iterable<? extends Module> modules) {
    return new InternalInjectorCreator()
        .stage(stage)
        .addModules(modules)
        .build();
  }

從這裏我們可以看出,Guice會利用應用指定的Module配合InternalInjectorCreator構建出一個Injector對象。

Guice源碼學習(一)基本原理