Github 主頁:https://github.com/google/guice
API:http://google.github.io/guice/api-docs/4.0/javadoc/
Guice (pronounced 'juice') is a lightweight dependency injection framework for Java 6 and above, brought to you by Google. 一個輕量級的依賴注入框架。
關于 Spring 的依賴注入忍饰,請參見 Spring 依賴注入 DI 的方式
一個 Google Guice 示例參見 Guide to Google Guice
例如我們有一個 Communication
類挤悉,它實際上是利用 Communicator
來真正的發(fā)送消息诽嘉。
添加 Maven 的依賴:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.0</version>
</dependency>
我們首先定義 Communicator
接口挺举,和它的一個實現(xiàn)類 DefaultCommunicatorImpl
:
public interface Communicator {
boolean sendMessage(String message);
}
public class DefaultCommunicatorImpl implements Communicator {
public boolean sendMessage(String message) {
System.out.println("Sending Message + " + message);
return true;
}
}
隨后我們通過 @Inject
注解來在 Communication
類中注入 Communicator
類的依賴:
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import java.util.logging.Logger;
public class Communication {
@Inject
private Communicator communicator;
public Communication(Boolean keepRecords) {
if (keepRecords) {
System.out.println("Message logging enabled");
}
}
public boolean sendMessage(String message) {
communicator.sendMessage(message);
return true;
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BasicModule());
Communication comms = injector.getInstance(Communication.class);
comms.sendMessage("hello world");
}
}
在 main()
中,可以看到我們通過 Injector
得到了一個 Communication
實例询刹,隨后調用了 sendMessage()
方法谜嫉。
那么 BasicModule
類又是怎么樣的呢?
The Module is the basic unit of definition of bindings. 定義依賴綁定的基本單元凹联。
- 它需要繼承
AbstractModule
類 - 它將
Communication
綁定了到一個實例 Instance沐兰,傳入?yún)?shù)true
到構造方法 - 它將
Communicator
綁定了到一個具體的實現(xiàn)DefaultCommunicatorImpl
import com.google.inject.AbstractModule;
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
// 表明:當需要 Communicator 這個變量時,我們注入 DefaultCommunicatorImpl 的實例作為依賴
bind(Communicator.class).to(DefaultCommunicatorImpl.class);
bind(Communication.class)
.toInstance(new Communication(true));
}
}
運行輸出如下:
Message logging enabled
Sending Message + hello world
可以看到蔽挠,Guice 通過代碼的形式來注入并管理依賴住闯,而不是通過 XML 配置文件的形式,這個與 Spring 不太一樣澳淑。
我們也可通過 @Provides
注解來在 BasicModule
中定義依賴:
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
bind(Communication.class)
.toInstance(new Communication(true));
}
@Provides
@Singleton
public Communicator getCommunicator() {
return new DefaultCommunicatorImpl();
}
}
其中 @Singleton
注解表明這個依賴的 Scope 是單例比原,它是延時加載的 lazily initiated。
如果我們對一個依賴進行了多次綁定杠巡,例如:
@Provides
@Singleton
public Communicator getCommunicator() {
return new DefaultCommunicatorImpl();
}
@Provides
@Singleton
public Communicator getCommunicatorOneMoreTime() {
return new DefaultCommunicatorImpl();
}
運行時會拋出如下的異常:
1) A binding to demo.guice.Communicator was already configured at demo.guice.BasicModule.getCommunicator().
at demo.guice.BasicModule.getCommunicator(BasicModule.java:17)
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:466)
at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
at com.google.inject.Guice.createInjector(Guice.java:96)
at com.google.inject.Guice.createInjector(Guice.java:73)
at com.google.inject.Guice.createInjector(Guice.java:62)
假如我們現(xiàn)在有了 Communicator
接口的另外一種實現(xiàn) AnotherCommunicatorImpl
:
public class AnotherCommunicatorImpl implements Communicator {
public boolean sendMessage(String message) {
System.out.println("Another Sending Message + " + message);
return true;
}
}
同時我們在 Communication
類中需要同時依賴于原有的 DefaultCommunicatorImpl
和新定義的 AnotherCommunicatorImpl
量窘,例如:
public class Communication {
@Inject
private Communicator communicator;
@Inject
private Communicator anotherCommunicator;
public Communication(Boolean keepRecords) {
if (keepRecords) {
System.out.println("Message logging enabled");
}
}
public boolean sendMessage(String message) {
communicator.sendMessage(message);
anotherCommunicator.sendMessage(message);
return true;
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BasicModule());
Communication comms = injector.getInstance(Communication.class);
comms.sendMessage("hello world");
}
}
那么我們在 BasicModule
應該怎么定義這種綁定呢?
如果我們嘗試添加另外一個 @Provides
方法氢拥,返回 AnotherCommunicatorImpl
蚌铜,例如:
@Provides
@Singleton
public Communicator getCommunicator() {
return new DefaultCommunicatorImpl();
}
@Provides
@Singleton
public Communicator getAnotherCommunicator() {
return new AnotherCommunicatorImpl();
}
則會有如下的異常:
Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:
1) A binding to demo.guice.Communicator was already configured at demo.guice.BasicModule.getCommunicator().
at demo.guice.BasicModule.getAnotherCommunicator(BasicModule.java:23)
這里我們需要通過 @Named
注解提供為屬性賦值的功能。
首先在注入綁定的時候使用 @Named
注解:
@Inject
@Named("communicator")
private Communicator communicator;
@Inject
@Named("anotherCommunicator")
private Communicator anotherCommunicator;
隨后在定義綁定的時候使用 @Named
注解:
@Provides
@Singleton
@Named("communicator")
public Communicator getCommunicator() {
return new DefaultCommunicatorImpl();
}
@Provides
@Singleton
@Named("anotherCommunicator")
public Communicator getAnotherCommunicator() {
return new AnotherCommunicatorImpl();
}
運行結果如下:
Message logging enabled
Sending Message + hello world
Another Sending Message + hello world
Guice 的工作原理
總的來說:
-
Guice
:整個框架的門面 -
Injector
:一個依賴的管理上下文 -
Binder
:一個接口和實現(xiàn)的綁定 -
Module
:一組Binder
-
Provider
:bean 的提供者 -
Key
:Binder
中對應一個Provider
-
Scope
:Provider
的作用域
每個綁定 Binding<T>
的結構如下:
public interface Binding<T> extends Element {
Key<T> getKey();
Provider<T> getProvider();
同時它繼承了 Element
嫩海,里面包含了 Source:
public interface Element {
Object getSource();
可以看出每個綁定 Binding<T>
冬殃,包含一個鍵 Key<T>
和 一個提供者 Provider
:
-
鍵
Key<T>
唯一地確定每一個綁定。 鍵Key<T>
包含了客戶代碼所依賴的類型以及一個可選的標注叁怪。你可以使用標注來區(qū)分指向同一類型的多個綁定审葬。- 例如,上述的代碼中奕谭,
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 會幫你創(chuàng)建它的實例展箱。
- 你也可以給 Guice 一個你要綁定的類的實例旨枯。
- 你還可以實現(xiàn)你自己的
Provider<T>
,Guice 可以向其中注入依賴關系混驰。 - 例如攀隔,上述的代碼中,就有一個提供者是
class demo.guice.DefaultCommunicatorImpl
每個綁定還有一個可選的作用域栖榨。缺省情況下綁定沒有作用域昆汹,Guice 為每一次注入創(chuàng)建一個新的對象。一個定制的作用域可以使你控制 Guice 是否創(chuàng)建新對象婴栽。例如满粗,你可以使用 為每一個 HttpSession 創(chuàng)建一個實例。
我們可以通過如下的方式遍歷每一個綁定 Binding<T>
Injector injector = Guice.createInjector(new BasicModule());
Map<Key<?>, Binding<?>> bindings = injector.getBindings();
for (Map.Entry<Key<?>, Binding<?>> bingingEntry : bindings.entrySet()) {
Binding binging = bingingEntry.getValue();
Key key = binging.getKey();
Provider provider = binging.getProvider();
System.out.println("Key: " + key.toString());
System.out.println("Provider: " + provider.get().getClass());
System.out.println("************");
}
輸出如下:
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);
的過程:
先根據(jù)指定的類來 new Key()
愚争,Key
包括類信息 XXX.class
和注解信息映皆,XXX.class
的 hashcode
和注解的 hashcode
決定了 Key
的 hashcode
挤聘,getProvider
時是根據(jù) 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 最明顯的一個區(qū)別键闺,Spring 是在裝載配置文件的時候把該注入的地方都注入完,而 Guice 呢澈驼,則是在使用的時候去注射艾杏,運行效率和靈活性高。
-
類耦合度:
- Spring 耦合度低盅藻,強調類非侵入购桑,以外部化的方式處理依賴關系,類里邊是很干凈的氏淑,在配置文件里做文章
- Guice 耦合度高勃蜘,代碼級的標注,DI 標記
@inject
侵入代碼中假残,耦合到了類層面上來