Guice快速入門教程


Github 主頁:https://github.com/google/guice

API:http://google.github.io/guice/api-docs/4.0/javadoc/

From: https://www.jianshu.com/p/7fba7b43146a

Guice (pronounced 'juice') is a lightweight dependency injection framework for Java 6 and above, brought to you by Google. 一個輕量級的依賴注入框架。

關於 Spring 的依賴注入,請參見 Spring 依賴注入 DI 的方式


例如我們有一個 Communication 類,它實際上是利用 Communicator 來真正的發送消息。

添加 Maven 的依賴:

1 <dependency>
2     <groupId>com.google.inject</groupId>
3     <artifactId>guice</artifactId>
4     <version>4.0</version>
5 </dependency>

 

 

我們首先定義 Communicator 接口,和它的一個實現類 DefaultCommunicatorImpl

1 public interface Communicator {
2     boolean sendMessage(String message);
3 }

 

1 public class DefaultCommunicatorImpl implements Communicator {
2     public boolean sendMessage(String message) {
3         System.out.println("Sending Message + " + message);
4         return true;
5     }
6 }

 

隨后我們通過 @Inject 注解來在 Communication 類中注入 Communicator 類的依賴:

 1 import com.google.inject.Guice;
 2 import com.google.inject.Inject;
 3 import com.google.inject.Injector;
 4 
 5 import java.util.logging.Logger;
 6 
 7 public class Communication {
 8     @Inject
 9     private Communicator communicator;
10 
11     public Communication(Boolean keepRecords) {
12         if (keepRecords) {
13             System.out.println("Message logging enabled");
14         }
15     }
16 
17     public boolean sendMessage(String message) {
18         communicator.sendMessage(message);
19         return true;
20     }
21 
22     public static void main(String[] args) {
23         Injector injector = Guice.createInjector(new BasicModule());
24 
25         Communication comms = injector.getInstance(Communication.class);
26 
27         comms.sendMessage("hello world");
28     }
29 }

 

main() 中,可以看到我們通過 Injector 得到了一個 Communication 實例,隨后調用了 sendMessage() 方法。

那么 BasicModule 類又是怎么樣的呢?

The Module is the basic unit of definition of bindings. 定義依賴綁定的基本單元。

  • 它需要繼承 AbstractModule
  • 它將 Communication 綁定了到一個實例 Instance,傳入參數 true 到構造方法
  • 它將 Communicator 綁定了到一個具體的實現 DefaultCommunicatorImpl
 1 import com.google.inject.AbstractModule;
 2 
 3 public class BasicModule extends AbstractModule {
 4 
 5     @Override
 6     protected void configure() {
 7         // 表明:當需要 Communicator 這個變量時,我們注入 DefaultCommunicatorImpl 的實例作為依賴
 8         bind(Communicator.class).to(DefaultCommunicatorImpl.class);
 9 
10         bind(Communication.class)
11                 .toInstance(new Communication(true));
12     }
13 }

 

運行輸出如下:

Message logging enabled
Sending Message + hello world

可以看到,Guice 通過代碼的形式來注入並管理依賴,而不是通過 XML 配置文件的形式,這個與 Spring 不太一樣。

我們也可通過 @Provides 注解來在 BasicModule 中定義依賴:

 1 public class BasicModule extends AbstractModule {
 2     @Override
 3     protected void configure() {
 4         bind(Communication.class)
 5                 .toInstance(new Communication(true));
 6     }
 7 
 8     @Provides
 9     @Singleton
10     public Communicator getCommunicator() {
11         return new DefaultCommunicatorImpl();
12     }
13 }

 

其中 @Singleton 注解表明這個依賴的 Scope 是單例,它是延時加載的 lazily initiated。

如果我們對一個依賴進行了多次綁定,例如:

 1 @Provides
 2 @Singleton
 3 public Communicator getCommunicator() {
 4     return new DefaultCommunicatorImpl();
 5 }
 6 
 7 @Provides
 8 @Singleton
 9 public Communicator getCommunicatorOneMoreTime() {
10     return new DefaultCommunicatorImpl();
11 }

 

運行時會拋出如下的異常:

 1 1) A binding to demo.guice.Communicator was already configured at demo.guice.BasicModule.getCommunicator().
 2   at demo.guice.BasicModule.getCommunicator(BasicModule.java:17)
 3 
 4 1 error
 5     at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:466)
 6     at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
 7     at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
 8     at com.google.inject.Guice.createInjector(Guice.java:96)
 9     at com.google.inject.Guice.createInjector(Guice.java:73)
10     at com.google.inject.Guice.createInjector(Guice.java:62)

 

假如我們現在有了 Communicator 接口的另外一種實現 AnotherCommunicatorImpl

1 public class AnotherCommunicatorImpl implements Communicator {
2     public boolean sendMessage(String message) {
3         System.out.println("Another Sending Message + " + message);
4         return true;
5     }
6 }

 

同時我們在 Communication 類中需要同時依賴於原有的 DefaultCommunicatorImpl 和新定義的 AnotherCommunicatorImpl,例如:

 1 public class Communication {
 2 
 3     @Inject
 4     private Communicator communicator;
 5 
 6     @Inject
 7     private Communicator anotherCommunicator;
 8 
 9     public Communication(Boolean keepRecords) {
10         if (keepRecords) {
11             System.out.println("Message logging enabled");
12         }
13     }
14 
15     public boolean sendMessage(String message) {
16         communicator.sendMessage(message);
17 
18         anotherCommunicator.sendMessage(message);
19 
20         return true;
21     }
22 
23     public static void main(String[] args) {
24         Injector injector = Guice.createInjector(new BasicModule());
25 
26         Communication comms = injector.getInstance(Communication.class);
27 
28         comms.sendMessage("hello world");
29     }
30 }

 

那么我們在 BasicModule 應該怎么定義這種綁定呢?
如果我們嘗試添加另外一個 @Provides 方法,返回 AnotherCommunicatorImpl,例如:

 1 @Provides
 2 @Singleton
 3 public Communicator getCommunicator() {
 4     return new DefaultCommunicatorImpl();
 5 }
 6 
 7 @Provides
 8 @Singleton
 9 public Communicator getAnotherCommunicator() {
10     return new AnotherCommunicatorImpl();
11 }

 

則會有如下的異常:

1 Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:
2 
3 1) A binding to demo.guice.Communicator was already configured at demo.guice.BasicModule.getCommunicator().
4   at demo.guice.BasicModule.getAnotherCommunicator(BasicModule.java:23)

 

這里我們需要通過 @Named 注解提供為屬性賦值的功能。
首先在注入綁定的時候使用 @Named 注解:

1 @Inject
2 @Named("communicator")
3 private Communicator communicator;
4 
5 @Inject
6 @Named("anotherCommunicator")
7 private Communicator anotherCommunicator;

 

隨后在定義綁定的時候使用 @Named 注解:

 1 @Provides
 2 @Singleton
 3 @Named("communicator")
 4 public Communicator getCommunicator() {
 5     return new DefaultCommunicatorImpl();
 6 }
 7 
 8 @Provides
 9 @Singleton
10 @Named("anotherCommunicator")
11 public Communicator getAnotherCommunicator() {
12     return new AnotherCommunicatorImpl();
13 }

 

運行結果如下:

Message logging enabled
Sending Message + hello world
Another Sending Message + hello world

Guice 的工作原理

總的來說:

  • Guice:整個框架的門面
  • Injector:一個依賴的管理上下文
  • Binder:一個接口和實現的綁定
  • Module:一組 Binder
  • Provider:bean 的提供者
  • KeyBinder 中對應一個 Provider
  • ScopeProvider 的作用域

每個綁定 Binding<T> 的結構如下:

1 public interface Binding<T> extends Element {
2     Key<T> getKey();
3 
4     Provider<T> getProvider();

 

同時它繼承了 Element,里面包含了 Source:

1 public interface Element {
2     Object getSource();
3 }

 

可以看出每個綁定 Binding<T>,包含一個鍵 Key<T> 和 一個提供者 Provider

  • Key<T> 唯一地確定每一個綁定。 Key<T> 包含了客戶代碼所依賴的類型以及一個可選的標注。你可以使用標注來區分指向同一類型的多個綁定。

    • 例如,上述的代碼中,Communicator 類型的就有兩個鍵:
    • Key[type=demo.guice.Communicator, annotation=@com.google.inject.name.Named(value=communicator)]
    • Key[type=demo.guice.Communicator, annotation=@com.google.inject.name.Named(value=anotherCommunicator)]
  • 對於每一個提供者 Provider,它提供所需類型的實例:

    • 你可以提供一個類,Guice 會幫你創建它的實例。
    • 你也可以給 Guice 一個你要綁定的類的實例。
    • 你還可以實現你自己的 Provider<T>,Guice 可以向其中注入依賴關系。
    • 例如,上述的代碼中,就有一個提供者是 class demo.guice.DefaultCommunicatorImpl
  • 每個綁定還有一個可選的作用域。缺省情況下綁定沒有作用域,Guice 為每一次注入創建一個新的對象。一個定制的作用域可以使你控制 Guice 是否創建新對象。例如,你可以使用 為每一個 HttpSession 創建一個實例。

我們可以通過如下的方式遍歷每一個綁定 Binding<T>

 1 Injector injector = Guice.createInjector(new BasicModule());
 2 
 3 Map<Key<?>, Binding<?>> bindings = injector.getBindings();
 4 
 5 for (Map.Entry<Key<?>, Binding<?>> bingingEntry : bindings.entrySet()) {
 6 
 7     Binding binging = bingingEntry.getValue();
 8 
 9     Key key =  binging.getKey();
10     Provider provider = binging.getProvider();
11 
12     System.out.println("Key: " + key.toString());
13 
14     System.out.println("Provider: " + provider.get().getClass());
15 
16     System.out.println("************");
17 }

 

輸出如下:

Key: Key[type=com.google.inject.Stage, annotation=[none]] Provider: class com.google.inject.Stage ************ Key: Key[type=com.google.inject.Injector, annotation=[none]] Provider: class com.google.inject.internal.InjectorImpl ************ Key: Key[type=java.util.logging.Logger, annotation=[none]] Provider: class java.util.logging.Logger ************ Key: Key[type=demo.guice.Communication, annotation=[none]] Provider: class demo.guice.Communication ************ Key: Key[type=demo.guice.Communicator, annotation=@com.google.inject.name.Named(value=communicator)] Provider: class demo.guice.DefaultCommunicatorImpl ************ Key: Key[type=demo.guice.Communicator, annotation=@com.google.inject.name.Named(value=anotherCommunicator)] Provider: class demo.guice.AnotherCommunicatorImpl ************ 

injector.getInstance(XXX.class); 的過程:
先根據指定的類來 new Key()Key 包括類信息 XXX.class 和注解信息,XXX.classhashcode 和注解的 hashcode 決定了 KeyhashcodegetProvider 時是根據 Keyhashcode 來判斷是否是同一個Key,然后取到 Provider,由 Provider 提供最終的示例。
例如上面 Key[type=demo.guice.Communicator, annotation=@com.google.inject.name.Named(value=communicator)]Key[type=demo.guice.Communicator, annotation=@com.google.inject.name.Named(value=anotherCommunicator)]hashcode 就分別為 -1491509781349671560

Guice DI 與 Spring DI 的比較

參考 Guice與Spring的區別

  • 使用方式:

    • Spring 將類與類之間的關系隔離到 XML 中,由容器負責注入被調用的對象
    • Guice 不使用 XML,而是使用注解 Annotation
  • 運行效率:

    • Guice 使用注解 Annotation,cglib, 效率高,這是與與 Spring 最明顯的一個區別,Spring 是在裝載配置文件的時候把該注入的地方都注入完,而 Guice 呢,則是在使用的時候去注射,運行效率和靈活性高。
  • 類耦合度:

    • Spring 耦合度低,強調類非侵入,以外部化的方式處理依賴關系,類里邊是很干凈的,在配置文件里做文章
    • Guice 耦合度高,代碼級的標注,DI 標記 @inject 侵入代碼中,耦合到了類層面上來

 


免責聲明!

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



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