6 Guice的IOC容器
6.1 注入過程
在前面的教程中我們講了Guice注入例子,在使用上具體描述了Guice的注入過程。在下面的篇幅中我們從源碼級了解了Guice的注入過程。
我們從具體到抽象再到具體的深入了解Guice的內部運作機制 。
下面一張序列圖就是描述了Guice最簡單的一次注入過程。比如下面的例子是我們熟悉的。
1 public class HelloWorldDemo {
2 public static void main(String[] args) {
3 Injector inj = Guice.createInjector(new Module() {
4 @Override
5 public void configure(Binder binder) {
6 binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
7 }
8 });
9 HelloWorld hw = inj.getInstance(HelloWorld.class);
10 hw.sayHello();
11 //
12 }
13 }
從上面的圖像可以看到我們的核心是Guice如何將我們的實例注入到Injector中的,這樣客戶端才能在Injector查找我們需要的服務。
我們進入Guice的createInjector方法,看看Guice到底做了什么操作。
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, Module
modules) {
return createInjector(stage, Arrays.asList(modules));
}
public static Injector createInjector(Stage stage,
Iterable<? extends Module> modules) {
return new InjectorBuilder()
.stage(stage)
.addModules(modules)
.build();
}
從上面的代碼可以看到我們的Injector是被InjectorBuilder以Builder的模式構造出來的。同時我們也可以注入多個Module,并且默認情況下Guice是以Stage.DEVELOPMENT模式運行的。
在進入我們最核心的InjectorBuilder之前,我們先簡化下模型。所謂的IOC容器,或者說DI容器,我們可以看做是一個特殊的Map,這個Map能夠將我們的對象按照某種Key(鍵值)的方式存入,然后客戶端能夠根據Key來獲取我們的對象。因此為了了解Guice容器的內部結構,我們先要了解下Guice容器中存放一個對象的Key到底是什么。
6.2 容器Key
在Guice中用com.google.inject.Key<T>對象來描述一個實例可以對應的Key。
我們可以想象,如果我們以某種類型來從Map中獲取結果,那么對于同一種類型每次獲取的結果就一樣。看似滿足需求。但是如果某一種類型對應多種實例怎么辦?這種情況下我們就需要我們的Key不僅支持類型,還附帶另外一點點東西。Guice是完全基于Annotation的,沒有類似spring那樣唯一id的概念,于是在Guice中描述一個Key就是靠類型和注解來完成的。在基礎教程中我們看到了對于同一種類型,加了不同的注解獲取的就是不同的實例。
在Java中每一個對象都有一個類型的概念,即使私有類型比如int,boolean也是有類型的概念,但是自從Java 5推出泛型以后,一直沒有一種描述泛型的類型。比如說List<String>在Java中使用List類型來描述的。但是盡管JVM有運行時擦除泛型的特點,卻又有能夠獲取編譯前類型的特性,因此實際上List<String>和List<Integer>對于我們來說應該是兩種不同的類型。
Guice自創造了一種描述類型的方式,包括泛型類型。在Guice中使用com.google.inject.TypeLiteral<T>類描述所有的類型(包括泛型類型)。我們可以寫一個小的例子來看看。
/**
* $Id: TypeLiteralDemo.java 110 2010-01-08 03:06:53Z xylz $
* xylz study project (www.imxylz.cn)
*/
package cn.imxylz.study.guice.inner;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.google.inject.TypeLiteral;
/** a demo for using {@link TypeLiteral}
* @author xylz (www.imxylz.cn)
* @version $Rev: 110 $
*/
public class TypeLiteralDemo {
public static void main(String[] args) throws Exception{
//
System.out.println(String.format("guice type:%s", TypeLiteral.get(Boolean.class)));
System.out.println(String.format("java type:%s", Boolean.class));
System.out.println();
//
System.out.println(String.format("guice type:%s", TypeLiteral.get(int.class)));
System.out.println(String.format("java type:%s", int.class));
System.out.println();
//
System.out.println(String.format("guice type:%s", new TypeLiteral<Map<Integer, String>>(){}));
System.out.println(String.format("java type:%s", new HashMap<Integer,String>().getClass()));
System.out.println();
//
Method m = Map.class.getMethod("keySet", new Class[0]);
System.out.println(String.format("java type:%s", m.getReturnType()));
System.out.println(String.format("java generic type:%s", m.getGenericReturnType()));
System.out.println(String.format("guice type:%s", TypeLiteral.get(m.getGenericReturnType())));
System.out.println();
TypeLiteral<Map<Integer, String>> mapType = new TypeLiteral<Map<Integer, String>>() {};
System.out.println(String.format("guice type:%s", mapType.getReturnType(m)));
}
}
下面是一次輸出結果。
guice type:java.lang.Boolean
java type:class java.lang.Boolean
guice type:int
java type:int
guice type:java.util.Map<java.lang.Integer, java.lang.String>
java type:class java.util.HashMap
java type:interface java.util.Set
java generic type:java.util.Set<K>
guice type:java.util.Set<K>
guice type:java.util.Set<java.lang.Integer>
從上面的結果可以看出,Java通過一些反射機制描述了部分泛型的類型(使用java.lang.reflect.ParameterizedType來描述,其它類型使用java.lang.reflect.Type來描述,注意Class是實現了Type接口的),但是并不完整,因此Guice重寫了這部分。
在上面的類圖中我們可以看到,一個key是包含一個類型描述(TypeLiteral)和一個AnnotationStrategy的。AnnotationStrategy是由Annotation以及Annotation的類型組成。而TypeLiteral包含私有類型Class和對Key的引用的。
總之在Guice中是通過類型描述和注解(Key)來完整實例描述的,通過一個Key就我們能夠從Guice容器(Injector)中獲取我們需要的實例,至于這個實例是單個實例還是一組實例(Set或者Map類型的實例),后面會繼續探討。
上一篇:Google Guice 高級教程01 - 源碼目錄
下一篇:待續
©2009-2014 IMXYLZ
|求賢若渴