Guice源碼學習(一)基本原理
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源碼學習(一)基本原理