The best classes in any application are the ones that do stuff: the BarcodeDecoder
, the KoopaPhysicsEngine
, and the AudioStreamer
. These classes have dependencies; perhaps a BarcodeCameraFinder
, DefaultPhysicsEngine
, and an HttpStreamer
.
To contrast, the worst classes in any application are the ones that take up space without doing much at all: the BarcodeDecoderFactory
, the CameraServiceLoader
, and the MutableContextWrapper
. These classes are the clumsy duct tape that wires the interesting stuff together.
Dagger is a replacement for these FactoryFactory
classes that implements the Dependency injection design pattern without the burden of writing the boilerplate. It allows you to focus on the interesting classes. Declare dependencies, specify how to satisfy them, and ship your app.
By building on standard javax.inject
annotations (JSR-330), each class is easy to test. You don't need a bunch of boilerplate just to swap the RpcCreditCardService
out for a FakeCreditCardService
.
Dependency injection isn't just for testing. It also makes it easy to create reusable, interchangeable modules. You can share the same AuthenticationModule
across all of your apps. And you can run DevLoggingModule
during development and ProdLoggingModule
in production to get the right behavior in each situation.
Why Dagger 2 is Different
Dependency injection frameworks have existed for years with a whole variety of APIs for configuring and injecting. So, why reinvent the wheel? Dagger 2 is the first to implement the full stack with generated code. The guiding principle is to generate code that mimics the code that a user might have hand-written to ensure that dependency injection is a simple, traceable and performant as it can be. For more background on the design, watch this talk (slides) by +Gregory Kick.
Using Dagger
We'll demonstrate dependency injection and Dagger by building a coffee maker. For complete sample code that you can compile and run, see Dagger's coffee example.
Declaring Dependencies
Dagger constructs instances of your application classes and satisfies their dependencies. It uses the javax.inject.Inject
annotation to identify which constructors and fields it is interested in.
Use @Inject
to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
...
}
Dagger can inject fields directly. In this example it obtains a Heater
instance for the heater
field and a Pump
instance for the pump
field.
class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
...
}
If your class has @Inject
-annotated fields but no @Inject
-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the @Inject
annotation to indicate that Dagger may create instances as well.
Dagger also supports method injection, though constructor or field injection are typically preferred.
Classes that lack @Inject
annotations cannot be constructed by Dagger.
Satisfying Dependencies
By default, Dagger satisfies each dependency by constructing an instance of the requested type as described above. When you request a CoffeeMaker
, it'll obtain one by calling new CoffeeMaker()
and setting its injectable fields.
But @Inject
doesn't work everywhere:
- Interfaces can't be constructed.
- Third-party classes can't be annotated.
- Configurable objects must be configured!
For these cases where @Inject
is insufficient or awkward, use an @Provides
-annotated method to satisfy a dependency. The method's return type defines which dependency it satisfies.
For example, provideHeater()
is invoked whenever a Heater
is required:
@Provides Heater provideHeater() {
return new ElectricHeater();
}
It's possible for @Provides
methods to have dependencies of their own. This one returns a Thermosiphon
whenever a Pump
is required:
@Provides Pump providePump(Thermosiphon pump) {
return pump;
}
All @Provides
methods must belong to a module. These are just classes that have an @Module
annotation.
@Module
class DripCoffeeModule {
@Provides Heater provideHeater() {
return new ElectricHeater();
}
@Provides Pump providePump(Thermosiphon pump) {
return pump;
}
}
By convention, @Provides
methods are named with a provide
prefix and module classes are named with a Module
suffix.
Building the Graph
The @Inject
and @Provides
-annotated classes form a graph of objects, linked by their dependencies. Calling code like an application's main
method or an Android Application
accesses that graph via a well-defined set of roots. In Dagger 2, that set is defined by an interface with methods that have no arguments and return the desired type. By applying the @Component
annotation to such an interface and passing the module types to the module
parameter, Dagger 2 then fully generates an implementation of that contract.
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
The implementation has the same name as the interface prefixed with Dagger_
. Obtain an instance by invoking the builder()
method on that implementation and use the returned builder to set dependencies and build()
a new instance.
CoffeeShop coffeeShop = Dagger_CoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
Any module with an accessible default constructor can be elided as the builder will construct an instance automatically if none is set. If all dependencies can be constructed in that manner, the generated implementation will also have a create()
method that can be used to get a new instance without having to deal with the builder.
CoffeeShop coffeeShop = Dagger_CoffeeShop.create();
Now, our CoffeeApp
can simply use the Dagger-generated implementation of CoffeeShop
to get a fully-injected CoffeeMaker
.
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = Dagger_CoffeeShop.create();
coffeeShop.maker().brew();
}
}
Now that the graph is constructed and the entry point is injected, we run our coffee maker app. Fun.
$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
Singletons and Scoped Bindings
Annotate an @Provides
method or injectable class with @Singleton
. The graph will use a single instance of the value for all of its clients.
@Provides @Singleton Heater provideHeater() {
return new ElectricHeater();
}
The @Singleton
annotation on an injectable class also serves as documentation. It reminds potential maintainers that this class may be shared by multiple threads.
@Singleton
class CoffeeMaker {
...
}
Since Dagger 2 associates scoped instances in the graph with instances of component implementations, the components themselves need to declare which scope they intend to represent. For example, it wouldn't make any sense to have a @Singleton
binding and a @RequestScoped
binding in the same component because those scopes have different lifecycles and thus must live in components with different lifecycles. Declaring that a component is associated with a given scope, simply apply the scope annotation to the component interface.
@Component(modules = DripCoffeeModule.class)
@Singleton
interface CoffeeShop {
CoffeeMaker maker();
}
Lazy injections
Sometimes you need an object to be instantiated lazily. For any binding T
, you can create a Lazy<T>
which defers instantiation until the first call to Lazy<T>
's get()
method. If T
is a singleton, then Lazy<T>
will be the same instance for all injections within the ObjectGraph
. Otherwise, each injection site will get its own Lazy<T>
instance. Regardless, subsequent calls to any given instance of Lazy<T>
will return the same underlying instance of T
.
class GridingCoffeeMaker {
@Inject Lazy<Grinder> lazyGrinder;
public void brew() {
while (needsGrinding()) {
// Grinder created once on first call to .get() and cached.
lazyGrinder.get().grind();
}
}
}
Provider injections
Sometimes you need multiple instances to be returned instead of just injecting a single value. While you have several options (Factories, Builders, etc.), one option is to inject a Provider<T>
instead of just T
. A Provider<T>
invokes the binding logic for T
each time .get()
is called. If that binding logic is an @Inject
constructor, a new instance will be created, but a @Provides
method has no such guarantee.
class BigCoffeeMaker {
@Inject Provider<Filter> filterProvider;
public void brew(int numberOfPots) {
...
for (int p = 0; p < numberOfPots; p++) {
maker.addFilter(filterProvider.get()); //new filter every time.
maker.addCoffee(...);
maker.percolate();
...
}
}
}
Note: Injecting Provider<T>
has the possibility of creating confusing code, and may be a design smell of mis-scoped or mis-structured objects in your graph. Often you will want to use a factory or a Lazy<T>
or re-organize the lifetimes and structure of your code to be able to just inject a T
. Injecting Provider<T>
can, however, be a life saver in some cases. A common use is when you must use a legacy architecture that doesn't line up with your object's natural lifetimes (e.g. servlets are singletons by design, but only are valid in the context of request-specfic data).
Qualifiers
Sometimes the type alone is insufficient to identify a dependency. For example, a sophisticated coffee maker app may want separate heaters for the water and the hot plate.
In this case, we add a qualifier annotation. This is any annotation that itself has a @Qualifier
annotation. Here's the declaration of @Named
, a qualifier annotation included in javax.inject
:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
You can create your own qualifier annotations, or just use @Named
. Apply qualifiers by annotating the field or parameter of interest. The type and qualifier annotation will both be used to identify the dependency.
class ExpensiveCoffeeMaker {
@Inject @Named("water") Heater waterHeater;
@Inject @Named("hot plate") Heater hotPlateHeater;
...
}
Supply qualified values by annotating the corresponding @Provides
method.
@Provides @Named("hot plate") Heater provideHotPlateHeater() {
return new ElectricHeater(70);
}
@Provides @Named("water") Heater provideWaterHeater() {
return new ElectricHeater(93);
}
Dependencies may not have multiple qualifier annotations.
Compile-time Validation
The Dagger annotation processor is strict and will cause a compiler error if any bindings are invalid or incomplete. For example, this module is installed in a component, which is missing a binding for Executor
:
@Module
class DripCoffeeModule {
@Provides Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
When compiling it, javac
rejects the missing binding:
[ERROR] COMPILATION ERROR :
[ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.
Fix the problem by adding an @Provides
-annotated method for Executor
to any of the modules in the component. While @Inject
, @Module
and @Provides
annotations are validated individually, all validation of the relationship between bindings happens at the @Component
level. Dagger 1 relied strictly on @Module
-level validation (which may or may not have reflected runtime behavior), but Dagger 2 elides such validation (and the accompanying configuration parameters on @Module
) in favor of full graph validation.
Compile-time Code Generation
Dagger's annotation processor may also generate source files with names like CoffeeMaker$$Factory.java
or CoffeeMaker$$MembersInjector.java
. These files are Dagger implementation details. You shouldn't need to use them directly, though they can be handy when step-debugging through an injection.
Using Dagger In Your Build
You will need to include the dagger-2.0-SNAPSHOT.jar
in your application's runtime. In order to activate code generation you will need to include dagger-compiler-2.0-SNAPSHOT.jar
in your build at compile time.
In a Maven project, one would include the runtime in the dependencies section of your pom.xml
, and the dagger-compiler
artifact as a dependency of the compiler plugin:
<dependencies>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>2.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<dependencies>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
License
Copyright 2014 Google, Inc.
Copyright 2012 Square, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.