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
来真正的发送消息。
添加 Maven 的依赖:
com.google.inject
guice
4.0
我们首先定义 Communicator
接口,和它的一个实现类 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,传入参数true
到构造方法 - 它将
Communicator
绑定了到一个具体的实现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)
假如我们现在有了 Communicator
接口的另外一种实现 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
:一个接口和实现的绑定 -
Module
:一组Binder
-
Provider
:bean 的提供者 -
Key
:Binder
中对应一个Provider
-
Scope
:Provider
的作用域
每个绑定 Binding
的结构如下:
public interface Binding extends Element {
Key getKey();
Provider getProvider();
同时它继承了 Element
,里面包含了 Source:
public interface Element {
Object getSource();
可以看出每个绑定 Binding
,包含一个键 Key
和 一个提供者 Provider
:
-
键
Key
唯一地确定每一个绑定。 键Key
包含了客户代码所依赖的类型以及一个可选的标注。你可以使用标注来区分指向同一类型的多个绑定。- 例如,上述的代码中,
Communicator
类型的就有两个键: Key[type=demo.guice.Communicator, [email protected](value=communicator)]
Key[type=demo.guice.Communicator, [email protected](value=anotherCommunicator)]
- 例如,上述的代码中,
-
对于每一个提供者
Provider
,它提供所需类型的实例:- 你可以提供一个类,Guice 会帮你创建它的实例。
- 你也可以给 Guice 一个你要绑定的类的实例。
- 你还可以实现你自己的
Provider
,Guice 可以向其中注入依赖关系。 - 例如,上述的代码中,就有一个提供者是
class demo.guice.DefaultCommunicatorImpl
每个绑定还有一个可选的作用域。缺省情况下绑定没有作用域,Guice 为每一次注入创建一个新的对象。一个定制的作用域可以使你控制 Guice 是否创建新对象。例如,你可以使用 为每一个 HttpSession 创建一个实例。
我们可以通过如下的方式遍历每一个绑定 Binding
Injector injector = Guice.createInjector(new BasicModule());
Map, Binding>> bindings = injector.getBindings();
for (Map.Entry, 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, [email protected](value=communicator)]
Provider: class demo.guice.DefaultCommunicatorImpl
************
Key: Key[type=demo.guice.Communicator, [email protected](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, [email protected](value=communicator)]
和 Key[type=demo.guice.Communicator, [email protected](value=anotherCommunicator)]
的 hashcode
就分别为 -1491509781
和 349671560
。
Guice DI 与 Spring DI 的比较
参考 Guice与Spring的区别
-
使用方式:
- Spring 将类与类之间的关系隔离到 XML 中,由容器负责注入被调用的对象
- Guice 不使用 XML,而是使用注解 Annotation
-
运行效率:
- Guice 使用注解 Annotation,cglib, 效率高,这是与与 Spring 最明显的一个区别,Spring 是在装载配置文件的时候把该注入的地方都注入完,而 Guice 呢,则是在使用的时候去注射,运行效率和灵活性高。
-
类耦合度:
- Spring 耦合度低,强调类非侵入,以外部化的方式处理依赖关系,类里边是很干净的,在配置文件里做文章
- Guice 耦合度高,代码级的标注,DI 标记
@inject
侵入代码中,耦合到了类层面上来