本章節繼續討論依賴注入的其他話題,包括作用域(scope,這里有一個與線程綁定的作用域例子)、立即初始化(Eagerly Loading Bindings)、運行階段(Stage)、選項注入(Optional Injection)等等。
1.3.5 Scope(作用域)
在1.1章節中我們初步了解了對象的單例模式,在Guice中提供了一些常見的作用域,比如對于單例模式有下面兩個作用域。
com.google.inject.Scopes.SINGLETON
com.google.inject.Scopes.NO_SCOPE
在使用上,可以使用Module的bind來實現,看下面的例子。
1 public class ScopeDemo {
2 public static void main(String[] args) {
3
4 Service service = Guice.createInjector(new Module() {
5 @Override
6 public void configure(Binder binder) {
7 binder.bind(Service.class).to(WwwService.class).in(Scopes.SINGLETON);
8 }
9 }).getInstance(Service.class);
10 service.execute();
11 }
12 }
13
14
當然單例模式還可以似乎用@Singleton注解。
在com.google.inject.binder.ScopedBindingBuilder.in(Scope)方法中,一個Scope除了可以使上面的SINGLETION和NO_SCOPE外,還可以是自己定義的Scope。下面的例子演示了一個與線程綁定的Scope例子。
1 /**
2 * $Id: ThreadScopeDemo.java 90 2009-12-25 08:12:21Z xylz $
3 * xylz study project (www.imxylz.cn)
4 */
5 package cn.imxylz.study.guice.inject.more;
6
7 import com.google.inject.Binder;
8 import com.google.inject.Guice;
9 import com.google.inject.Injector;
10 import com.google.inject.Key;
11 import com.google.inject.Module;
12 import com.google.inject.Provider;
13 import com.google.inject.Scope;
14
15 /** a demo with thread-scope
16 * @author xylz (www.imxylz.cn)
17 * @version $Rev: 90 $
18 */
19 public class ThreadScopeDemo {
20
21 static class ThreadServiceScope implements Scope {
22
23 static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
24
25 @Override
26 public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
27 return new Provider<T>() {
28 @Override
29 public T get() {
30 T instance = (T) threadLocal.get();
31 if (instance == null) {
32 instance = unscoped.get();
33 threadLocal.set(instance);
34 }
35 return instance;
36 }
37 };
38 }
39
40 @Override
41 public String toString() {
42 return "Scopes.ThreadServiceScope";
43 }
44 }
45
46 public static void main(String[] args) {
47 final Injector inj=Guice.createInjector(new Module() {
48 @Override
49 public void configure(Binder binder) {
50 binder.bind(Service.class).to(WwwService.class).in(new ThreadServiceScope());
51 }
52 });
53 for(int i=0;i<3;i++) {
54 new Thread("Thread-"+i) {
55 public void run() {
56 for(int m=0;m<3;m++) {
57 System.out.println(String.format("%s-%d:%d",//
58 getName()//
59 ,m//
60 ,inj.getInstance(Service.class).hashCode()));
61 try {
62 Thread.sleep(50L);
63 } catch (Exception e) {
64 }
65 }
66 }
67 }.start();
68 }
69 }
70 }
71
注意,這里用到了《Google Guice 入門教程03 - 依賴注入》的中的兩個類Service和WwwService。在本例中ThreadServiceScope類是一個與線程綁定的作用域(利用ThreadLocal特性),當當前線程中沒有構造一個對象的時候先構造一個出來,然后放入線程上下文中,以后每次都從線程中獲取對象。第50行是將WwwService服務以ThreadServiceScope的作用域綁定到Service服務上。第57-60行輸出當前對象的hashCode,如果此類是同一對象的話就應該輸出相同的hashCode。為了看到效果,我們使用3個線程,每個線程輸出三次來看結果。
Thread-0-0:18303751
Thread-1-0:23473608
Thread-2-0:21480956
Thread-1-1:23473608
Thread-0-1:18303751
Thread-2-1:21480956
Thread-1-2:23473608
Thread-2-2:21480956
Thread-0-2:18303751
我們看到對于同一個線程(比如說Thread-0)的三次都輸出了相同的對象(hashCode為18303751),而與線程2和線程3的hashCode不同。
(特別說明:如果兩個線程輸出了同一個hashCode不必驚慌,那是因為可能前一個線程生成的對象的地址空間被GC釋放了,結果下一個線程使用了上一個線程的相同空間,所以這里使用Thread.sleep來降低這種可能性)
事實上在guice-servlet-2.0.jar中有與request和session綁定的scope。
com.google.inject.servlet.ServletScopes.REQUEST
com.google.inject.servlet.ServletScopes.SESSION
1.3.6 Eagerly Loading Bindings (立即初始化)
除了可以綁定scope外,對象默認在第一次調用時被創建,也即所謂的延時加載,Guice也允許對象在注入到Guice容器中時就被創建出來(顯然這是針對單例模式才有效)。
1 public class EagerSingletonDemo {
2
3 public EagerSingletonDemo() {
4 System.out.println(" constuctor:"+System.nanoTime());
5 }
6 void doit() {
7 System.out.println(" doit:"+System.nanoTime());
8 }
9 public static void main(String[] args) throws Exception{
10 Injector inj = Guice.createInjector(new Module() {
11 @Override
12 public void configure(Binder binder) {
13 binder.bind(EagerSingletonDemo.class).asEagerSingleton();
14 }
15 });
16 System.out.println("before call:"+System.nanoTime());
17 Thread.sleep(100L);
18 inj.getInstance(EagerSingletonDemo.class).doit();
19 }
20 }
結果輸出如下:
constuctor:26996967388652
before call:26996967713635
doit:26997069993702
可以看到我們的對象在調用getInstance之前就已經被構造出來了。
1.3.7 Stages (運行階段)
Guice還有一個特效,可以指定Guice運行模式來控制Guice的加載速度。在com.google.inject.Stage枚舉中提供了TOOL,DEVELOPMENT,PRODUCTION三種模式。
TOOL描述的是帶有IDE等插件的運行模式;DEVELOPMENT是指在開發階段只加載自己需要的功能(對于非立即初始化單例對象采用延后加載),這樣來降低加載不需要功能的時間;而PRODUCTION模式是指完全加載所有功能(對于單例對象采用立即加載方式),這樣可以更早的發現問題,免得等需要某些功能的時候才發現問題(要知道我們某些功能可能需要特定的條件才能觸發)。
其實只有比較多的單例對象,并且單例對象構造比較耗時的情況下才能有用。大部分情況下這點性能可能都忽略不計了。
默認情況下Guice采用DEVELOPMENT模式。
1.3.8 Optional Injection (選項注入 )
選項注入描述的是如果不能從Guice容器中注入一個對象,那么可以使用一個默認的對象。看下面的例子。
1 public class OptionalInjectionDemo {
2 @Inject(optional=true)
3 Service service = new WwwService();
4 public static void main(String[] args) {
5 Guice.createInjector(new Module() {
6 public void configure(Binder binder) {
7 //binder.bind(Service.class).to(HomeService.class);
8 }
9 }).getInstance(OptionalInjectionDemo.class).service.execute();
10 }
11 }
上述例子中第2行描述的是選項注入,如果不能從Guice容器中獲取一個Service服務那么就使用默認的WwwService,否則就是用獲取的服務。如果將第7行注釋去掉我們就可以看到實際上調用的是HomeService服務了。
到此為止,Guice依賴注入的基本教程就學習完了,下面的章節我們進入經典的AOP教程學習。
上一篇:Google Guice 入門教程03 - 依賴注入(3)
下一篇:Google Guice 入門教程05 - AOP(面向切面編程)
©2009-2014 IMXYLZ
|求賢若渴