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 的提供者 -
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
侵入代码中,耦合到了类层面上来