Dependency Injection

Dependency Injection #

Introduction #

As Wikipedia puts it:

Dependency injection is a technique whereby one object supplies the dependencies of another object.

If class A requires instances of classes B and C, B and C are considered dependencies of A. When instantiating class A, you can either supply these dependencies yourself or rely on something called a dependency container to do this for you. A dependency container holds all the dependencies of your classes and injects them into your classes when they are required.

The dependency injection pattern is a powerful design pattern that makes for more flexible software design and easier testing.

In Diego, dependency injection is implemented using Google Guice.

💡 You can read more about this design pattern on the Guice Wiki.

Using the dependency container #

You can access the dependency container in one of two ways:

  1. Use the @Inject annotation
  2. Use the inject() method from the UsesDependencyService trait

Using the @Inject annotation #

The main way of getting objects from the dependency container is by using the @com.google.Inject annotation in your classes. A good example of where this is useful is when you want to inject a Service or Database into your application:

class MyApplication extends Application {
    private final Database db
    
    @Inject
    MyApplication(Database db) {
        this.db = db
    }
    
    void configure(Router router) {
        router.get('/', (req, res) -> {
            // Use the db object here
        })
    }
}

You can place the @Inject annotation on constructors (ideal), methods or (least ideal) fields. All injection sites must be marked as public.

Using the inject() method #

Another, more direct way, of creating instances of classes with dependencies is by accessing the dependency container directly. To get access to the dependency container, have your class use the UsesDependencyService trait, which will give you access to the inject(Class<?>) method (as seen in the Database chapter).

class MyScheduledJob implements UsesDependecyService {
    private Database db = inject(Database)
}

Some classes such as Applicationuse this trait out of the box, so you don’t need to explicitly use this trait.

Adding modules to the dependency container #

You can add your custom modules to the dependency container. First, you create a Module (tutorial here) and tell the dependency container to load it when creating your application using the app.modules configuration key.

app.modules = ["modules.MyModule"]

Providers in your module will have access to:

  • The root Vertx instance;
  • The loaded application settings via the Settings singleton;
  • The configured ObjectMapper singleton; and
  • The configured BodyHandler singleton

Further reading #

If you would like to learn more about Guice you can look at the official Guice wiki and Baeldung’s Guide to Google Guice.

Next: Testing