Skip to content

Dependency Injection with Dagger

Furkan Kürşat Danışmaz edited this page Nov 11, 2018 · 3 revisions

Source Folder: dagger

Tech Stack:

  • Dagger

Very Lightweight Dependency Injection Framework - Dagger

Spring Framework is a great library that comes in handy for many cases. It helps to solve many problems and lets you build a robust and more maintainable code. However, it is a little bit kind of heavy. If you intend to use Spring just for dependency injection, you may consider using other lightweight IoC frameworks like Guice or Dagger.

For example, in a serverless architecture, your functions need to boot-up as fast as possible and the speed & memory usage matters as they cost money to you.

If you search for IoC benchmarks on the internet or build your own, you will find out that the Dagger is the most suitable option when the performance is the most critical thing that you should consider. In this story, I will show how to use Dagger with an example.

A Maven Java Project with Dagger

You need to add the Dagger dependency to the dependency list:

<dependency>
    <groupId>com.google.dagger</groupId>
    <artifactId>dagger</artifactId>
    <version>${dagger.version}</version>
</dependency>

Then you need to configure the annotation processor:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven.plugins.maven-compiler-plugin.version}</version>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>com.google.dagger</groupId>
                <artifactId>dagger-compiler</artifactId>
                <version>${dagger.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

The Dagger @Component

Dagger generates code (at compile time) about how to create objects that are to be injected when needed based on the configurations you make in your code.

During compile time, Dagger searches for @Component annotated interfaces. When it finds one, it generates a class with the same name prefixed with Dagger implementing that interface and a static create method that creates an instance of it. Those @Component annotated interfaces are the places where you need to define the injectable objects.

@Component
public interface IoCContainer {
    
    MyService myService();

}

You just need to write an abstract method without any parameter and with a return type of your desired object. If you don't do that, Dagger won't generate any code that creates object of that type.

Note that, the class that you declare to be injectable needs @Inject annotation on top of its constructor. You'll get compiler error otherwise.

public class MyService {

    @Inject
    public MyService() {
        ...
    }

    ...
}

You can also inject objects to injectable objects:

public class MyService {

    private MyBusinessLogic businessLogic;

    @Inject
    public MyService(MyBusinessLogic businessLogic) {
        this.businessLogic = businessLogic;
        ...
    }

    ...
}

public class MyBusinessLogic {

    @Inject
    public MyBusinessLogic() {
    }

    ...
}

Notice that, again I decorated the constructor of the second class with @Inject annotation. Without it, dagger won't be able to inject it.

How to Use

As I said above, for each @Component annotated interface, you'll get a class with the same name prefixed with Dagger implementing that interface. For the example given above, we'll get DaggerIoCContainer class and we can create an instance of it with its create() method:

public static void main(String[] args) {
    IoCContainer ioc = DaggerIoCContainer.create();
    MyService myService = ioc.myService();
}

Modules

The above example shows the simplest case of injecting dependencies. But what about:

  • when we need to inject an instance of an interface?
  • when we need to inject a class of a third-party library that we cannot modify (Decorate its construtor with @Inject)?
  • when we need to inject that needs to be configured first?

In such cases, we need to help Dagger and define how to construct objects to be injected with @Provides annotated methods.

public interface MyService2 {
    ...
}

public class MyService2Impl implements MyService2 {
    ...
    
    @Inject
    public MyServiceImpl() {
    }

    ...
}

@Module
public class SampleDaggerModule {

    @Provides
    @Singleton
    public static MyService2 myService2() {
        return new MyService2Impl();
    }

    ...
}

We can define how to construct objects that we need and configure them before being injected to other objects. To do so, we just need to decorate the methods that returns such objects with @Provides annotation and decorate the containing class with @Module. All @Provides annotated methods should belong to a @Module annotated class.

To make the dagger be aware of such module, we need to add it to the modules property of @Component annotation.

@Singleton
@Component(modules = SampleDaggerModule.class)
public interface IoCContainer {
   ...
}

Final Words

  • Dagger is a very lightweight dependency injection framework
  • It is compatible with JSR-330 standard injection annotations.
  • It doesn't support private field injection
  • We need to decorate the constructor of the classes that we want to be injectable with the @Inject annotation.
  • We need to write @Module annotated classes with @Provides annotated methods when @Inject is not sufficient.

Clone this wiki locally