http://code.google.com/p/google-guice/
Google公司的Bob lee開發的輕量級IoC容器,其特點是:
1、速度快,號稱是spring的100倍速度
2、無配置文件,實用JDK5.0的annotation描述組件依賴,簡單,而且有編譯器檢查和重構支持
3、簡單,代碼量很少
http://code.google.com/p/google-guice/wiki/SpringComparison
這是Google guice和spring IoC容器的對比
另外xwork2.0已經集成了Google guice容器了。
http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8
這是Google guice的快速入門文檔,用起來挺簡單的。
Google Guice范例解說之使用入門
本文通過范例簡單地介紹Google Guice的使用,通過下面的范例我們可以知道,Google Guice的使用非常簡單。
Google Guice需要使用JDK1.5以上java環境。
下載Google Guice之后,
有以下幾個文件:
aopalliance.jar
guice-1.0.jar
guice-servlet-1.0.jar
guice-spring-1.0.jar
guice-struts2-plugin-1.0.jar
本例只使用到guice-1.0.jar文件,將其加入到class path中。
下面簡單地介紹范例:
范例1:使用com.google.inject.Module接口實現類
文件名 說明
文件名 | 說明 |
HelloGuice.java | 業務邏輯接口定義文件 |
HelloGuiceImpl.java | 業務邏輯接口實現文件 |
HelloGuiceModule.java | 該文件必須實現com.google.inject.Module接口 |
TestGuice.java | 測試文件 |
HelloGuice.java
package com.test.guice;
public interface HelloGuice {
public void sayHello();
}
HelloGuiceImpl.java
package com.test.guice;
public class HelloGuiceImpl implements HelloGuice {
public void sayHello() {
System.out.println("Hello Guice!");
}
}
HelloGuiceModule.java
package com.test.guice;
import com.google.inject.Binder;
import com.google.inject.Module;
public class HelloGuiceModule implements Module {
public void configure(Binder binder) {
binder.bind(HelloGuice.class).to(HelloGuiceImpl.class);
}
}
TestGuice.java
package com.test.guice;
import junit.framework.TestCase;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class TestGuice extends TestCase {
public void testHelloGuice() {
Injector injector = Guice.createInjector(new HelloGuiceModule());
HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
helloGuice.sayHello();
}
}
運行TestGuice,打印出:
Hello Guice!
范例2:使用Java Annotation
我們也可以直接為HelloGuice加上@ImplementedBy注釋,而省略掉對com.google.inject.Module的實現。
HelloGuice.java
package com.test.guice;
import com.google.inject.ImplementedBy;
@ImplementedBy(HelloGuiceImpl.class)
public interface HelloGuice {
public void sayHello();
}
TestGuice.java
package com.test.guice;
import junit.framework.TestCase;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class TestGuice extends TestCase {
public void testHelloGuice() {
//Injector injector = Guice.createInjector(new HelloGuiceModule());
Injector injector = Guice.createInjector();
HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
helloGuice.sayHello();
}
}
HelloGuiceModule.java不再需要。其余的文件內容不變。
運行TestGuice,打印出:
Hello Guice!
摘自 :http://hi.baidu.com/changzhiwin/blog/item/1e8251861feb553466096e36.html
1. 依賴注入
1.1 類依賴注入
所謂的綁定就是將一個接口綁定到具體的類中,這樣客戶端不用關心具體的實現,而只需要獲取相應的接口完成其服務即可。
HelloWorld.java
2
3 String sayHello();
4 }
5
然后是具體的實現,HelloWorldImpl.java
2
3 @Override
4 public String sayHello() {
5 return " Hello, world! " ;
6 }
7 }
8
寫一個測試例子看看,HelleWorldTest.java
2
3 @Test
4 public void testSayHello() {
5 Injector inj = Guice.createInjector( new Module() {
6 @Override
7 public void configure(Binder binder) {
8 binder.bind(HelloWorld. class ).to(HelloWorldImpl. class );
9 }
10 });
11 HelloWorld hw = inj.getInstance(HelloWorld. class );
12 Assert.assertEquals(hw.sayHello(), " Hello, world! " );
13 }
14 }
15
這個例子非常簡單,通俗的將就是將一個HelloWorldImpl的實例與HelloWorld關聯起來,當想Guice獲取一個HelloWorld實例的時候,Guice就返回一個HelloWorldImpl的實例,然后我們就可以調用HelloWorld服務的方法了。
問題(1)HelloWorld是單例的么?測試下。
2 Assert.assertEquals(hw.sayHello(), " Hello, world! " );
3 HelloWorld hw2 = inj.getInstance(HelloWorld. class );
4 System.out.println(hw.hashCode() + " -> " + hw2.hashCode());
5 Assert.assertEquals(hw.hashCode(), hw2.hashCode());
解答(1)測試結果告訴我們,HelloWorld不是單例的,每次都會返回一個新的實例。
問題(2)HelloWorld的實例是HelloWorldImpl么?可以強制轉型么?
HelloWorld hw = inj.getInstance(HelloWorld. class );
System.out.println(hw.getClass().getName());
解答(2),結果輸出cn.imxylz.study.guice.helloworld.HelloWorldImpl,看來確實只是返回了一個正常的實例,並沒有做過多的轉換和代理。
問題(3),如果綁定多個實現到同一個接口上會出現什么情況?
2 @Override
3 public String sayHello() {
4 return " Hello world again. " ;
5 }
6 }
binder.bind(HelloWorld. class ).to(HelloWorldImplAgain. class );
解答(3),很不幸,Guice目前看起來不允許多個實例綁定到同一個接口上了。
com.google.inject.CreationException: Guice creation errors:
1) A binding to cn.imxylz.study.guice.helloworld.HelloWorld was already configured at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28).
at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:29)
問題(4),可以綁定一個實現類到實現類么?
1 Injector inj = Guice.createInjector( new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorldImpl. class ).to(HelloWorldImpl. class );
5 }
6 });
7 HelloWorld hw = inj.getInstance(HelloWorldImpl. class );
8 System.out.println(hw.sayHello());
非常不幸,不可以自己綁定到自己。
1) Binding points to itself.
at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28)
我們來看看bind的語法。
也就是說只能綁定一個類的子類到其本身。改造下,改用子類替代。
2
3 @Override
4 public String sayHello() {
5 return " @HelloWorldSubImpl " ;
6 }
7 }
8
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorldImpl. class ).to(HelloWorldSubImpl. class );
5 }
6 });
7 HelloWorldImpl hw = inj.getInstance(HelloWorldImpl. class );
8 System.out.println(hw.sayHello());
太好了,支持子類綁定,這樣即使我們將一個實現類發布出去了(盡管不推薦這么做),我們在后期仍然有辦法替換實現類。
使用bind有一個好處,由於JAVA 5以上的泛型在編譯器就確定了,所以可以幫我們檢測出綁定錯誤的問題,而這個在配置文件中是無法檢測出來的。
這樣看起來Module像是一個Map,根據一個Key獲取其Value,非常簡單的邏輯。
問題(5),可以綁定到我們自己構造出來的實例么?
解答(5)當然可以!看下面的例子。
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorld. class ).toInstance( new HelloWorldImpl());
5 }
6 });
7 HelloWorld hw = inj.getInstance(HelloWorld. class );
8 System.out.println(hw.sayHello());
問題(6),我不想自己提供邏輯來構造一個對象可以么?
解答(6),可以Guice提供了一個方式(Provider<T>),允許自己提供構造對象的方式。
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorld. class ).toProvider( new Provider < HelloWorld > () {
5 @Override
6 public HelloWorld get() {
7 return new HelloWorldImpl();
8 }
9 });
10 }
11 });
12 HelloWorld hw = inj.getInstance(HelloWorld. class );
13 System.out.println(hw.sayHello());
問題(7),實現類可以不經過綁定就獲取么?比如我想獲取HelloWorldImpl的實例而不通過Module綁定么?
解答(7),可以,實際上Guice能夠自動尋找實現類。
HelloWorld hw = inj.getInstance(HelloWorldImpl. class );
System.out.println(hw.sayHello());
問題(8),可以使用注解方式完成注入么?不想手動關聯實現類。
解答(8),好,Guice提供了注解的方式完成關聯。我們需要在接口上指明此接口被哪個實現類關聯了。
2 public interface HelloWorld {
3
4 String sayHello();
5 }
6
HelloWorld hw = inj.getInstance(HelloWorld. class );
System.out.println(hw.sayHello());
事實上對於一個已經被注解的接口我們仍然可以使用Module來關聯,這樣獲取的實例將是Module關聯的實例,而不是@ImplementedBy注解關聯的實例。這樣仍然遵循一個原則,手動優於自動。
問題(9)再回頭看問題(1)怎么綁定一個單例?
1 Injector inj = Guice.createInjector( new Module() {
2
3 @Override
4 public void configure(Binder binder) {
5 binder.bind(HelloWorld. class ).to(HelloWorldImplAgain. class ).in(Scopes.SINGLETON);
6 }
7 });
8 HelloWorld hw = inj.getInstance(HelloWorld. class );
9 HelloWorld hw2 = inj.getInstance(HelloWorld. class );
10 System.out.println(hw.hashCode() + " -> " + hw2.hashCode());
11
可以看到現在獲取的實例已經是單例的,不再每次請求生成一個新的實例。事實上Guice提供兩種Scope,com.google.inject.Scopes.SINGLETON和com.google.inject.Scopes.NO_SCOPE,所謂沒有scope即是每次生成一個新的實例。
對於自動注入就非常簡單了,只需要在實現類加一個Singleton注解即可。
1 @Singleton
2 public class HelloWorldImpl implements HelloWorld {
3
4 @Override
5 public String sayHello() {
6 return " Hello, world! " ;
7 }
8 }
9
附:【前沿】本教程的依賴注入部分基於老菜鳥叮咚的教程,原文在此http://www.family168.com/tutorial/guice/html/。原文主要基於Google Guice 1.0版本的,本文基於Google Guice 2.0版本進行學習和討論。
1.2 屬性注入(Field Inject)
1.2.1 基本屬性注入
首先來看一個例子。Service.java
2 public interface Service {
3 void execute();
4 }
ServiceImpl.java
2 @Override
3 public void execute() {
4 System.out.println( " This is made by imxylz (www.imxylz.cn). " );
5 }
6 }
FieldInjectDemo.java
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 71 $
4 */
5 public class FieldInjectDemo {
6 @Inject
7 private Service servcie;
8 public Service getServcie() {
9 return servcie;
10 }
11 public static void main(String[] args) {
12 FieldInjectDemo demo = Guice.createInjector().getInstance(FieldInjectDemo. class );
13 demo.getServcie().execute();
14 }
15 }
這個例子比較簡單。具體來說就是將接口Service通過@Inject注解注入到FieldInjectDemo類中,然后再FieldInjectDemo類中使用此服務而已。當然Service服務已經通過@ImplementedBy注解關聯到ServiceImpl 類中,每次生成一個新的實例(非單例)。注意,這里FieldInjectDemo類沒有通過Module等關聯到Guice中,具體可以查看《》。
意料之中得到了我們期待的結果。
同樣,我們通過問答的方式來加深理解(注意,入門教程我們只是強調怎么使用,至於原理和底層的思想我們放到高級教程中再談)。
問題(1):可以自己構造FieldInjectDemo 對象而不通過Guice么?
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 73 $
4 */
5 public class FieldInjectDemo2 {
6 @Inject
7 private Service servcie;
8 public Service getServcie() {
9 return servcie;
10 }
11 public static void main(String[] args) {
12 FieldInjectDemo2 fd = new FieldInjectDemo2();
13 fd.getServcie().execute();
14 }
15 }
就像上面的例子中一樣,然后運行下看看?非常不幸,我們得到了一個誰都不喜歡的結果。
at cn.imxylz.study.guice.inject.FieldInjectDemo2.main(FieldInjectDemo2.java: 22 )
很顯然,由於FieldInjectDemo2不屬於Guice容器(暫且稱為容器吧)托管,這樣Service服務沒有機會被注入到FieldInjectDemo2類中。
問題(2):可以注入靜態屬性么?
看下面的代碼。
2 @Inject
3 private static Service servcie;
4 public static Service getServcie() {
5 return servcie;
6 }
7 public static void main(String[] args) {
8 FieldInjectDemo2 fd = Guice.createInjector().getInstance(FieldInjectDemo2. class );
9 FieldInjectDemo2.getServcie().execute();
10 }
11 }
很不幸!運行結果告訴我們Guice看起來還不支持靜態字段注入。
好了,上面兩個問題我們暫且放下,我們繼續學習其它注入功能。
1.2.2 構造函數注入(Constructor Inject)
繼續看例子。例子是說明問題的很好方式。
2 * $Id: ConstructorInjectDemo.java 75 2009-12-23 14:22:35Z xylz $
3 * xylz study project (www.imxylz.cn)
4 */
5 package cn.imxylz.study.guice.inject;
6
7 import com.google.inject.Guice;
8 import com.google.inject.Inject;
9
10 /** a demo with constructor inject
11 * @author xylz (www.imxylz.cn)
12 * @version $Rev: 75 $
13 */
14 public class ConstructorInjectDemo {
15
16 private Service service;
17 @Inject
18 public ConstructorInjectDemo(Service service) {
19 this .service = service;
20 }
21 public Service getService() {
22 return service;
23 }
24 public static void main(String[] args) {
25 ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo. class );
26 cid.getService().execute();
27 }
28
29 }
30
31
我們在構造函數上添加@Inject來達到自動注入的目的。構造函數注入的好處是可以保證只有一個地方來完成屬性注入,這樣可以確保在構造函數中完成一些初始化工作(盡管不推薦這么做)。當然構造函數注入的缺點是類的實例化與參數綁定了,限制了實例化類的方式。
問題(3):構造函數中可以自動注入多個參數么?
2
3 private Service service;
4 private HelloWorld helloWorld;
5 @Inject
6 public ConstructorInjectDemo(Service service,HelloWorld helloWorld) {
7 this .service = service;
8 this .helloWorld = helloWorld;
9 }
10 public Service getService() {
11 return service;
12 }
13 public HelloWorld getHelloWorld() {
14 return helloWorld;
15 }
16 public static void main(String[] args) {
17 ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo. class );
18 cid.getService().execute();
19 System.out.println(cid.getHelloWorld().sayHello());
20 }
21 }
22
23
非常完美的支持了多參數構造函數注入。當然了沒有必要寫多個@Inject,而且寫了的話不能通過編譯。
1.2.3 Setter注入(Setter Method Inject)
有了上面的基礎我們再來看Setter注入就非常簡單了,只不過在setter方法上增加一個@Inject注解而已。
2
3 private Service service;
4
5 @Inject
6 public void setService(Service service) {
7 this .service = service;
8 }
9
10 public Service getService() {
11 return service;
12 }
13
14 public static void main(String[] args) {
15 SetterInjectDemo sid = Guice.createInjector().getInstance(SetterInjectDemo. class );
16 sid.getService().execute();
17 }
18
19 }
20
21
好了我們再回頭看問題2的靜態注入(static inject)。下面的例子演示了如何注入一個靜態的字段。
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 78 $
4 */
5 public class StaticFieldInjectDemo {
6
7 @Inject
8 private static Service service;
9
10 public static void main(String[] args) {
11 Guice.createInjector( new Module() {
12 @Override
13 public void configure(Binder binder) {
14 binder.requestStaticInjection(StaticFieldInjectDemo. class );
15 }
16 });
17 StaticFieldInjectDemo.service.execute();
18 }
19 }
20
21
非常棒!上面我們並沒有使用Guice獲取一個StaticFieldInjectDemo實例(廢話),實際上static字段(屬性)是類相關的,因此我們需要請求靜態注入服務。但是一個好處是在外面看起來我們的服務沒有Guice綁定,甚至client不知道(或者不關心)服務的注入過程。
再回到問題(1),參考上面靜態注入的過程,我們可以使用下面的方式來注入實例變量的屬性。
2
3 @Inject
4 private Service service;
5 public static void main(String[] args) {
6 final InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
7 Guice.createInjector( new Module() {
8 @Override
9 public void configure(Binder binder) {
10 binder.requestInjection(ifid);
11 }
12 });
13 ifid.service.execute();
14 }
15 }
16
17
實際上這里有一種簡便的方法來注入字段,實際上此方法也支持Setter注入。
2
3 @Inject
4 private Service service;
5 public static void main(String[] args) {
6 InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
7 Guice.createInjector().injectMembers(ifid);
8 ifid.service.execute();
9 }
10 }
11
12
好了既然是入門教程,我們就不討論更深層次的東西了。
轉自 http://www.blogjava.net/xylz/archive/2009/xylz/archive/2009/12/23/307092.html