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 的提供者 -
Key
:Binder
中對應一個Provider
-
Scope
:Provider
的作用域
每個綁定 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.class
的 hashcode
和注解的 hashcode
決定了 Key
的 hashcode
,getProvider
時是根據 Key
的 hashcode
來判斷是否是同一個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
就分別為 -1491509781
和 349671560
。
Guice DI 與 Spring DI 的比較
-
使用方式:
- Spring 將類與類之間的關系隔離到 XML 中,由容器負責注入被調用的對象
- Guice 不使用 XML,而是使用注解 Annotation
-
運行效率:
- Guice 使用注解 Annotation,cglib, 效率高,這是與與 Spring 最明顯的一個區別,Spring 是在裝載配置文件的時候把該注入的地方都注入完,而 Guice 呢,則是在使用的時候去注射,運行效率和靈活性高。
-
類耦合度:
- Spring 耦合度低,強調類非侵入,以外部化的方式處理依賴關系,類里邊是很干凈的,在配置文件里做文章
- Guice 耦合度高,代碼級的標注,DI 標記
@inject
侵入代碼中,耦合到了類層面上來