Google Guice范例解說之使用入門


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

 

1       public   interface  HelloWorld {
2 
3          String sayHello();
4      }
5 

然后是具體的實現,HelloWorldImpl.java

 

1       public   class  HelloWorldImpl  implements  HelloWorld {
2 
3          @Override
4           public  String sayHello() {
5               return   " Hello, world! " ;
6          }
7      }
8 

寫一個測試例子看看,HelleWorldTest.java

 

 1       public   class  HelleWorldTest {
 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是單例的么?測試下。

 

1  HelloWorld hw  =  inj.getInstance(HelloWorld. class ); 
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),如果綁定多個實現到同一個接口上會出現什么情況?

 

1  public   class  HelloWorldImplAgain  implements  HelloWorld {
2      @Override
3       public  String sayHello() {
4           return   " Hello world again. " ;
5      }
6  }

 

binder.bind(HelloWorld. class ).to(HelloWorldImpl. class );
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的語法。

< T >  AnnotatedBindingBuilder < T >  bind(Class < T >  type);

 

 

ScopedBindingBuilder to(Class <?   extends  T >  implementation);

也就是說只能綁定一個類的子類到其本身。改造下,改用子類替代。

 

1       public   class  HelloWorldSubImpl  extends  HelloWorldImpl {
2 
3          @Override
4           public  String sayHello() {
5               return   " @HelloWorldSubImpl " ;
6          }
7      }
8 

 

1  Injector inj =   Guice.createInjector( new  Module() {
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)當然可以!看下面的例子。

 

1  Injector inj =   Guice.createInjector( new  Module() {
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>),允許自己提供構造對象的方式。

 

 1  Injector inj =   Guice.createInjector( new  Module() {
 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能夠自動尋找實現類。

 

Injector inj =   Guice.createInjector();
HelloWorld hw 
=  inj.getInstance(HelloWorldImpl. class );
System.out.println(hw.sayHello());

問題(8),可以使用注解方式完成注入么?不想手動關聯實現類。

解答(8),好,Guice提供了注解的方式完成關聯。我們需要在接口上指明此接口被哪個實現類關聯了。

 

1      @ImplementedBy(HelloWorldImpl. class )
2       public   interface  HelloWorld {
3 
4          String sayHello();
5      }
6 

 

Injector inj =   Guice.createInjector();
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

 

1  @ImplementedBy(ServiceImpl. class )
2  public   interface  Service {
3       void  execute();
4  }

ServiceImpl.java

 

1  public   class  ServiceImpl  implements  Service {
2      @Override
3       public   void  execute() {
4          System.out.println( " This is made by imxylz (www.imxylz.cn). " );
5      }
6  }

FieldInjectDemo.java

 

 1  /**  a demo with Field inject
 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么?

 

 1  /**  field inject demo2
 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  }

就像上面的例子中一樣,然后運行下看看?非常不幸,我們得到了一個誰都不喜歡的結果。

 

Exception in thread  " main "  java.lang.NullPointerException
    at cn.imxylz.study.guice.inject.FieldInjectDemo2.main(FieldInjectDemo2.java:
22 )

很顯然,由於FieldInjectDemo2不屬於Guice容器(暫且稱為容器吧)托管,這樣Service服務沒有機會被注入到FieldInjectDemo2類中。

問題(2):可以注入靜態屬性么?

看下面的代碼。

 

 1  public   class  FieldInjectDemo2 {
 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)

繼續看例子。例子是說明問題的很好方式。

 

 1       /**
 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):構造函數中可以自動注入多個參數么?

 

 1       public   class  ConstructorInjectDemo {
 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注解而已。

 

 1       public   class  SetterInjectDemo {
 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)。下面的例子演示了如何注入一個靜態的字段。

 

 1       /**  a demo for static field 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),參考上面靜態注入的過程,我們可以使用下面的方式來注入實例變量的屬性。

 

 1       public   class  InstanceFieldInjectDemo {
 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注入。

 

 1       public   class  InstanceFieldInjectDemo {
 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM