# Guice Mental Model *Learn about `Key`, `Provider` and how Guice is just a map* When you are reading about Guice, you often see many buzzwords ("Inversion of control", "Hollywood principle", "injection") that make it sound confusing. But underneath the jargon of dependency injection, the concepts aren't very complicated. In fact, you might have written something very similar already! This page walks through a simplified model of Guice's implementation, which should make it easier to think about how it works. **Note:** While this page doesn't assume any prior knowledge of Guice or dependency injection, it does assume a prior working knowledge of Java, including modern Java syntax with annotations, method references, and lambdas, as well as knowledge of common object-oriented programming principles and patterns. ## Guice is a map Fundamentally, Guice helps you create and retrieve objects for your application to use. These objects that your application needs are called **dependencies**. You can think of Guice as being a map[^guice-map]. Your application code declares the dependencies it needs, and Guice fetches them for you from its map. Each entry in the "Guice map" has two parts: * **Guice key**: a key in the map which is used to fetch a particular value from the map. * **Provider**: a value in the map which is used to create objects for your application. Guice keys and Providers are explained below. [^guice-map]: The actual implementation of Guice is far more complicated, but a map is a reasonable approximation for how Guice behaves. ### Guice keys Guice uses [`Key`] to identify a dependency that can be resolved using the "Guice map". [`Key`]: https://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/Key.html The `Greeter` class used in the [Getting Started Guice](GettingStarted#a-simple-guice-application) declares two dependencies in its constructor and those dependencies are represented as `Key` in Guice: * `@Message String` --> `Key` * `@Count int` --> `Key` The simplest form of a `Key` represents a type in Java: ```java // Identifies a dependency that is an instance of String. Key databaseKey = Key.get(String.class); ``` However, applications often have dependencies that are of the same type: ```java final class MultilingualGreeter { private String englishGreeting; private String spanishGreeting; MultilingualGreeter(String englishGreeting, String spanishGreeting) { this.englishGreeting = englishGreeting; this.spanishGreeting = spanishGreeting; } } ``` Guice uses [binding annotations](BindingAnnotations) to distinguish dependencies that are of the same type, that is to make the type more specific: ```java final class MultilingualGreeter { private String englishGreeting; private String spanishGreeting; @Inject MultilingualGreeter( @English String englishGreeting, @Spanish String spanishGreeting) { this.englishGreeting = englishGreeting; this.spanishGreeting = spanishGreeting; } } ``` `Key` with binding annotations can be created as: ```java Key englishGreetingKey = Key.get(String.class, English.class); Key spanishGreetingKey = Key.get(String.class, Spanish.class); ``` When an application calls `injector.getInstance(MultilingualGreeter.class)` to create an instance of `MultilingualGreeter`. This is the equivalent of doing: ```java // Guice internally does this for you so you don't have to wire up those // dependencies manually. String english = injector.getInstance(Key.get(String.class, English.class)); String spanish = injector.getInstance(Key.get(String.class, Spanish.class)); MultilingualGreeter greeter = new MultilingualGreeter(english, spanish); ``` To summarize: **Guice `Key` is a type combined with an optional binding annotation used to identify dependencies.** ### Guice `Provider`s Guice uses [`Provider`](https://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/Provider.html) to represent factories in the "Guice map" that are capable of creating objects to satisfy dependencies. `Provider` is an interface with a single method: ```java interface Provider { /** Provides an instance of T.**/ T get(); } ``` Each class that implements `Provider` is a bit of code that knows how to give you an instance of `T`. It could call `new T()`, it could construct `T` in some other way, or it could return you a precomputed instance from a cache. Most applications do not implement the `Provider` interface directly, they implement a `Module` to tell Guice how to configure the injector. Internally, Guice will create `Provider`s for all the objects it knows how to create. For example, the following Guice module creates two `Provider`s: ```java class DemoModule extends AbstractModule { @Provides @Count static Integer provideCount() { return 3; } @Provides @Message static String provideMessage() { return "hello world"; } } ``` * `Provider` that calls the `provideMessage` method and returns "hello world" * `Provider` that calls the `provideCount` method and returns `3` ## Using Guice There are two parts to using Guice: 1. **Configuration**: your application adds things into the "Guice map". 1. **Injection**: your application asks Guice to create and retrieve objects from the map. Configuration and injection are explained below. ### Configuration Guice maps are configured using Guice modules (and [Just-In-Time bindings](JustInTimeBindings.md)). A **Guice module** is a unit of configuration logic that adds things into the Guice map. There are two ways to do this: * Adding method annotations like `@Provides` * Using the Guice Domain Specific Language (DSL). Conceptually, these APIs simply provide ways to manipulate the Guice map. The manipulations they do are pretty straightforward. Here are some example translations, shown using Java 8 syntax for brevity and clarity: | Guice DSL syntax | Mental model | | ---------------------------------- | ---------------------------------------------------------------------------------- | | `bind(key).toInstance(value)` | `map.put(key, () -> value)`
(instance binding) | | `bind(key).toProvider(provider)` | `map.put(key, provider)`
(provider binding) | | `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) | `DemoModule` adds two entries into the Guice map: * `@Message String` --> `() -> DemoModule.provideMessage()` * `@Count Integer` --> `() -> DemoModule.provideCount()` ### Injection You don't *pull* things out of a map, you *declare* that you need them. This is the essence of dependency injection. If you need something, you don't go out and get it from somewhere, or even ask a class to return you something. Instead, you simply declare that you can't do your work without it, and rely on Guice to give you what you need. This model is backwards from how most people think about code: it's a more *declarative* model rather than an *imperative* one. This is why dependency injection is often described as a kind of *inversion of control* (IoC). Some ways of declaring that you need something: 1. An argument to an `@Inject` constructor: ```java class Foo { private Database database; @Inject Foo(Database database) { // We need a database, from somewhere this.database = database; } } ``` 1. An argument to a `@Provides` method: ```java @Provides Database provideDatabase( // We need the @DatabasePath String before we can construct a Database @DatabasePath String databasePath) { return new Database(databasePath); } ``` This example is intentionally the same as the example `Foo` class from [Getting Started Guide](GettingStarted#what-is-dependency-injection), adding only the `@Inject` annotation on the constructor, which marks the constructor as being available for Guice to use. ## Dependencies form a graph When injecting a thing that has dependencies of its own, Guice recursively injects the dependencies. You can imagine that in order to inject an instance of `Foo` as shown above, Guice creates `Provider` implementations that look like these: ```java class FooProvider implements Provider { @Override public Foo get() { Provider databaseProvider = guiceMap.get(Key.get(Database.class)); Database database = databaseProvider.get(); return new Foo(database); } } class ProvideDatabaseProvider implements Provider { @Override public Database get() { Provider databasePathProvider = guiceMap.get(Key.get(String.class, DatabasePath.class)); String databasePath = databasePathProvider.get(); return module.provideDatabase(databasePath); } } ``` Dependencies form a *directed graph*, and injection works by doing a depth-first traversal of the graph from the object you want up through all its dependencies. A Guice `Injector` object represents the entire dependency graph. To create an `Injector`, Guice needs to validate that the entire graph works. There can't be any "dangling" nodes where a dependency is needed but not provided.[^3] If the graph is invalid for any reason, Guice throws a `CreationException` that describes what went wrong. [^3]: The reverse case is not an error: it's fine to provide something even if nothing ever uses it—it's just dead code in that case. That said, just like any dead code, it's best to delete providers if nobody uses them anymore. ## What's next? Learn how to use [`Scopes`](Scopes) to manage the lifecycle of objects created by Guice and the many different ways to [add entries into the Guice map](Bindings).