zoukankan      html  css  js  c++  java
  • 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 的提供者
    • KeyBinder 中对应一个 Provider
    • ScopeProvider 的作用域

    每个绑定 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.classhashcode 和注解的 hashcode 决定了 KeyhashcodegetProvider 时是根据 Keyhashcode 来判断是否是同一个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 就分别为 -1491509781349671560

    Guice DI 与 Spring DI 的比较

    参考 Guice与Spring的区别

    • 使用方式:

      • Spring 将类与类之间的关系隔离到 XML 中,由容器负责注入被调用的对象
      • Guice 不使用 XML,而是使用注解 Annotation
    • 运行效率:

      • Guice 使用注解 Annotation,cglib, 效率高,这是与与 Spring 最明显的一个区别,Spring 是在装载配置文件的时候把该注入的地方都注入完,而 Guice 呢,则是在使用的时候去注射,运行效率和灵活性高。
    • 类耦合度:

      • Spring 耦合度低,强调类非侵入,以外部化的方式处理依赖关系,类里边是很干净的,在配置文件里做文章
      • Guice 耦合度高,代码级的标注,DI 标记 @inject 侵入代码中,耦合到了类层面上来
  • 相关阅读:
    关于@Mapper和@Repository的一点小理解
    记一下数据库的多表查询
    移动端时间控件 HTML5+ 的 pickDate 和 MUI 的 dtpicker 的使用与对比
    即时搜索:对于ios自带输入法输入中文时多次触发input事件的处理
    thead固定,tbody高度超出时滚动的实现方法
    数字位数不够,进行前补零的JS最简实现方案
    hbuilder ios 打包失败,无法导入p12证书的解决方案
    通过jQuery获取容器尺寸的方法height()、innerHeight()、outerHeight()的区别总结
    通过js添加的DOM节点的click事件绑定不上的解决方案以及IOS下click事件委派失效的解决方案
    vue.js项目构建——构建方式:vue-cli脚手架
  • 原文地址:https://www.cnblogs.com/winton-nfs/p/13958362.html
Copyright © 2011-2022 走看看