Google Guice 2:Mental Model

1. Preamble

1.1 Key-value pairs in Guice

  • The previous blog, "Google Guice 1: How to Implement Dependency Injection" , explained how to use Guice to implement dependency injection

  • Now, turning our attention back EmailModule, it inherits AbstractModule, overrides configure()the method

  • In the configure() method, by bind().to()binding EmailServicethe interface to a concrete implementation classQQEmailService

    public class EmailModule extends AbstractModule {
          
          
        @Override
        protected void configure() {
          
          
            bind(EmailService.class).to(QQEmailService.class);
        }
    }
    
  • When an application needs an instance of the EmailService type, Guice will automatically provide a QQEmailService instance

  • bind().to()This syntax is like defining a key-value pair, Key<EmailService> → \rightarrow Provider<EmailService>

  • The interface Provider<T>is defined as follows, where get()the method is responsible for providing an instance of T

    interface Provider<T> {
          
          
      /** Provides an instance of T.**/
      T get();
    }
    
  • Provider<EmailService>The pseudocode is as follows, providing a singleton EmailService instance

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

1.2 key-value pair → \rightarrow Guice map

  • These key-value pairs form a Guice map. Among them, Key can be used Key.get()to build

    Key<EmailService> emailService = Key.get(EmailService.class);
    
  • In Guice, obtaining instances explicitly or implicitly actually obtains bound objects from the Guice map

    • Explicitly obtain the instance: injector.getInstance(EmailClient.class), explicitly obtain the EmailClient instance
      // 等价于如下代码
      injector.getInstance(Key.get(EmailClient.class));
      
      // getInstance()的核心伪代码如下
      public EmailClient getInstance(Key<EmailClient> key) {
              
              
          Provider<EmailClient> provider = guiceMap.get(key); // key为Key<EmailClient>
          return provider.get();
      }
      
    • Obtaining an instance implicitly: When obtaining an instance of EmailClient explicitly, you need to obtain an instance of EmailService implicitly
      // 对应的核心伪代码:
      Provider<EmailService> provider = guiceMap.get(Key.get(EmailService.class)); // key为Key<EmailService>
      return provider.get();
      

2. Guice is a map

  • Through the previous study, it is not difficult to find that: Guice implements object management through map. It can be said that Guice is a mapit is Guice Mental Model:
  • Mental Model: mental model? mental model? In short, it is the modeling idea of ​​Guice
  • The two important links of using Guice are also around the map
    • Configuration: Configure Guice map in Guice module, or use Just-In-Timebindings to automatically create key-value mappings for objects
    • Injection: When the application needs it, Guice can create and retrieve objects from the map, thereby implementing dependency injection
  • Just-In-Time bindings, referred to as JIT bindings, also known as implicit bindingsimplicit binding

2.1 Configuration

  • The official explanation of the Guice module:

    A Guice module is a unit of configuration logic that adds things into the Guice map.
    Guice module is a configuration logic unit that can add things (key-value pairs, also called binding) to Guice map

  • In Guice module, you can use Guice DSL (Domain Specific Language) to configure Guice map

  • There are mainly the following DSLs (among them, the value part uses lambda expressions):

    Guice DSL syntax Mental model describe
    bind(key).toInstance(value) map.put(key, () -> value) instance binding, the binding between instance object and key
    bind(key).to(anotherKey) map.put(key, map.get(anotherKey)) linked binding, the binding relationship is like a chain, interlocking
    @Provides Foo provideFoo() {...} map.put(Key.get(Foo.class), module::provideFoo) Provider method binding, use @Providesto identify a method, and define the object creation logic in the method
    bind(key).toProvider(provider) map.put(key, provider) Provider binding, when the provider method binding becomes more and more complex, you can consider creating an independent Provider class to define the creation logic of the object
  • In "Google Guice 1: How to Implement Dependency Injection" , instance binding is used, EmailService-->() -> qqEmailService

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

2.2 Injection

  • The creation of objects depends on other objects. You do not need to actively obtain these objects (dependency) from the Guice map, but declare them directly you need something, and Guice will automatically inject these objects.
  • In Guice, the way to declare that the current class or method depends on other objects is as follows:
    • Use @Injectannotations to identify constructors, fields, and setter methods
      class Foo {
              
              
        private Database database;
      
        @Inject
        Foo(Database database) {
              
                // We need a database, from somewhere
          this.database = database;
        }
      }
      
    • @ProvidesThe identified method, for which parameters are passed in
      @Provides
      Database provideDatabase(@DatabasePath String databasePath) {
              
               // 在创建Databas前,需要注入 @DatabasePath 标识的String实例
        return new Database(databasePath);
      }
      
  • PS : From the above example, we can see that dependency injection not only occurs in dependent class, but also in Guice module

3. Summary

  • Guice implements several key points of dependency injection:
    • First, define the creation logic of the object in the form of key-value pairs and put it into the Guice map
    • Secondly, declare that dependency injection is required here by passing @Injector identifying the input parameters of the method@Provides
    • Finally, when obtaining an instance object (dependent obeject) that requires dependency injection, Guice Injector obtains the object (dependency) from the Guice map and injects it according to the dependency relationship, thereby completing the creation and initialization of the dependent obeject
  • Throughout the process, Guice is a map, which stores the object creation logic and is the entire GuiceMental Model

Guess you like

Origin blog.csdn.net/u014454538/article/details/128477881