Getting started with Dagger dependency injection
Update (2016): This was written for Dagger 1.x which is heavily different from Dagger 2.x. While the high-level concepts are still relevent, Dagger 2’s implementation is quite a bit different. If you’re learning Dagger 2, this article will probably be more confusing than helpful.
If you’re writing Java in 2014 and not using a dependency injector you’re doing yourself a huge disservice.
Dependency injection is a design pattern that requires an object’s external dependencies to be injected in rather then being created within that object itself (Wikipedia has a much better explanation).
For example, this coffee maker class requires coffee grinds and water. Here’s how most people would create it without dependency injection:
And here’s how you would create the Aeropress following the dependency injection pattern:
This might seem like a very minor change, but it gives us some huge advantages:
- Since these dependencies are external, we can now inject different types of water or coffee grinds depending on our needs.
- Writing unit tests? This pattern lets you easily create an Aeropress with mock water or mock coffee grinds.
- The Aeropress class is no longer responsible for finding or creating its dependencies.
That’s all there really is to dependency injection. Although this might seem simple now, eventually manual dependency injection becomes a little tedious.
Where are these objects created? Why is all this boilerplate required to wire things together? Luckily, Dagger solves this and more.
Enter Dagger
Dagger is a dependency injector that handles creating objects and fulfilling their dependencies for you.
Using Dagger, you create a module that has provider methods for all of your classes and it automatically fulfills dependencies for you.
For example, we can use this module for our coffee classes above:
Next, we create a new Dagger ObjectGraph with our module and get a new Aeropress instance:
This does all of the wiring of dependencies for us. Dagger knows how to create water and coffee grinds because of our provider methods in the module and the Aeropress provider takes both of these dependencies as parameters.
This is nice and convenient, but it’s a bit too verbose. Especially because none of these class require any special configuration. Thankfully, with annotations, we can remove nearly all of the boilerplate.
Inject Annotations
Dagger supports standard JSR-330 annotations so we can specify injected dependencies in code without the need for a module.
First we need to add some new annotations to the Aeropress class constructor:
This tells Dagger that this constructor can be used to build out this class.
An alternative to this approach is using the @Inject
annotation on fields themselves. In that case, the Aeropress class would look like this:
This is nice because it doesn’t require a constructor or setting any fields, but the fields with @Inject
annotations can’t be private. Both of these approaches are convenient though, but this approach should only be used when you don’t control the constructor for the class you are injecting dep.
We also (unfortunately) need to add empty constructors with @Inject
annotations to our Water and CoffeeGrinds classes. This might seem silly, but otherwise Dagger wouldn’t know how to create these objects without a module.
Now we can create an Aeropress object without needing all of the boilerplate module providers.
This might leave you asking, what’s the point of providers in the first place? Providers give you a great place to perform more complex configuration (for example, what if you have an Android app where you want to provide a different subclass of CoffeeGrinds
based on the device’s location?).
Advanced Dagger
This tutorial only gives a very basic introduction to dependency injection and Dagger. There’s a lot more you can do with Dagger including Android integration, more complex object graphs, leveraging Dagger for unit testing.
A good place to learn more is the Dagger website.