Google Guice 2:Mental Model

1. 序言

1.1 Guice中的键值对

  • 上一篇博客,《Google Guice 1:如何实现依赖注入》,讲解了如何利用Guice实现依赖注入

  • 现在,将注意力放回到EmailModule,它继承了AbstractModule,重写了configure()方法

  • 在configure()方法中,通过bind().to()EmailService接口绑定到具体的实现类QQEmailService

    public class EmailModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(EmailService.class).to(QQEmailService.class);
        }
    }
    
  • 当应用程序需要EmailService类型的实例时,Guice会自动提供一个QQEmailService实例

  • bind().to()这样的语法,就像定义了一个键值对,Key → \rightarrow Provider

  • 接口Provider的定义如下,其中get()方法负责提供一个T的实例

    interface Provider<T> {
      /** Provides an instance of T.**/
      T get();
    }
    
  • Provider的伪代码如下,提供一个单例的EmailService实例

    class EmailServiceProvider implements Provider<EmailService> {
        private static final EmailService instance = new QQEmailService();
        @Override
        public EmailService get() {
            return instance; 
        }
    }
    

1.2 键值对 → \rightarrow Guice map

  • 这些键值对,形成了Guice map。其中,Key可以使用Key.get()去构建

    Key<EmailService> emailService = Key.get(EmailService.class);
    
  • Guice中,显式或隐式获取实例,实际都是从Guice map中获取绑定好的对象

    • 显式获取实例:injector.getInstance(EmailClient.class),显式获取EmailClient实例
      // 等价于如下代码
      injector.getInstance(Key.get(EmailClient.class));
      
      // getInstance()的核心伪代码如下
      public EmailClient getInstance(Key<EmailClient> key) {
          Provider<EmailClient> provider = guiceMap.get(key); // key为Key
          return provider.get();
      }
      
    • 隐式获取实例:显式获取EmailClient实例时,需要隐式获取EmailService实例
      // 对应的核心伪代码:
      Provider<EmailService> provider = guiceMap.get(Key.get(EmailService.class)); // key为Key
      return provider.get();
      

2. Guice is a map

  • 通过前面的学习,不难发现:Guice通过map实现对象的管理,可以说,Guice is a map是Guice的Mental Model
  • Mental Model:心智模型?心理模型?总之,就是Guice的建模思路
  • 使用Guice的两个重要环节,也是围绕map展开的
    • Configuration:在Guice module中配置Guice map,或者使用Just-In-Time bindings,自动创建对象的键值映射
    • Injection:当应用程序需要时,Guice可以从map中创建并检索对象,从而实现依赖注入
  • Just-In-Time bindings,简称JIT bindings,又叫implicit bindings,隐式绑定

2.1 Configuration

  • Guice module的官方解释:

    A Guice module is a unit of configuration logic that adds things into the Guice map。
    Guice module是一个配置逻辑单元,可以将事物(键值对,又叫binding)添加到Guice map中

  • 在Guice module中,可以使用Guice DSL(Domain Specific Language ),配置Guice map

  • 主要有以下几种DSL(其中,value部分使用了lambda表达式):

    Guice DSL syntax Mental model 描述
    bind(key).toInstance(value) map.put(key, () -> value) instance binding,实例对象与key的绑定
    bind(key).to(anotherKey) map.put(key, map.get(anotherKey)) linked binding, 绑定关系就像链条,环环相扣
    @Provides Foo provideFoo() {...} map.put(Key.get(Foo.class), module::provideFoo) provider method binding,使用@Provides标识一个方法,并在方法中定义对象的创建逻辑
    bind(key).toProvider(provider) map.put(key, provider) provider binding,当provider method binding变得越来越复杂时,可以考虑创建一个独立的Provider类去定义对象的创建逻辑
  • 《Google Guice 1:如何实现依赖注入》中,使用的是instance binding,EmailService --> () -> qqEmailService

    bind(EmailService.class).to(QQEmailService.class);
    

2.2 Injection

  • 对象的创建依赖其他对象,你无需主动从Guice map中获取这些对象(dependency),而是直接声明you need something,Guice则会自动注入这些对象
  • Guice中,声明当前类或方法依赖其他对象的方式如下:
    • 使用@Inject注解标识构造函数、字段、setter方法
      class Foo {
        private Database database;
      
        @Inject
        Foo(Database database) {  // We need a database, from somewhere
          this.database = database;
        }
      }
      
    • @Provides标识的方法,为其传入参数
      @Provides
      Database provideDatabase(@DatabasePath String databasePath) { // 在创建Databas前,需要注入 @DatabasePath 标识的String实例
        return new Database(databasePath);
      }
      
  • PS:从上面的例子可知,依赖的注入不止发生在dependent class中,在Guice module中也存在依赖注入

3. 总结

  • Guice实现依赖注入的几个关键点:
    • 首先,以键值对的形式定义对象的创建逻辑,放入到Guice map中
    • 其次,通过@Inject@Provides标识方法的入参,来声明此处需要依赖注入
    • 最后,获取某个需要依赖注入的实例对象(dependent obeject)时,Guice Injector根据依赖关系,从Guice map中获取对象(dependency)并注入,从而完成dependent obeject的创建与初始化
  • 整个过程中,Guice is a map,存储了对象的创建逻辑,是整个Guice的Mental Model

你可能感兴趣的:(java相关,java)