Guice快速入门教程中,有哪些技巧和最佳实践?
摘要:Github 主页:https:github.comgoogleguice API:http:google.github.ioguiceapi-docs4.0javadoc From: https:www.jian
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 的比较
参考 Guice与Spring的区别
使用方式:
Spring 将类与类之间的关系隔离到 XML 中,由容器负责注入被调用的对象
Guice 不使用 XML,而是使用注解 Annotation
运行效率:
Guice 使用注解 Annotation,cglib, 效率高,这是与与 Spring 最明显的一个区别,Spring 是在装载配置文件的时候把该注入的地方都注入完,而 Guice 呢,则是在使用的时候去注射,运行效率和灵活性高。
类耦合度:
Spring 耦合度低,强调类非侵入,以外部化的方式处理依赖关系,类里边是很干净的,在配置文件里做文章
Guice 耦合度高,代码级的标注,DI 标记 @inject 侵入代码中,耦合到了类层面上来
