Fork me on GitHub

Dependency injection

Introduction

In regular code you usually would use a lot of new MyObject(…) statements. But that is usually bad because you don't have any outside control of the instances. And this makes the code hard to test. It also makes it hard to give the MyObject different behaviors - let's say for testing and in production.

You can of course use the factory pattern. But there, too, you find a strong coupling between the factory and the produced instances.

Dependency injection is in fact a factory pattern on steroids. There are different implementations of the dependency injection pattern available, but Ninja uses Google's Guice for dependency injection.

The nice thing about Guice is the fact that there is one central point where you can declare which interface should be implemented by what class. Whether it is a Singleton or what Factory (aka Provider) should create that class.

Guice in Ninja

By convention Ninja will look for a Java file at conf/Module.java. That module is a regular Guice module that will be automatically loaded when your application starts up. This file is 100% pure Guice and you can use all Guice goodies. If needed, the constructor of this module can take a NinjaProperties object as argument, but it's not mandatory.

An example

A typical usecase it to have an interface that specifies certain methods and glue these interface to implementations via Guice. Let's say we have an interface that returns a message:

public interface GreetingService {
	String hello();
}

and the following implementation:

public class GreetingServiceImpl implements GreetingService {
    
    @Override
	public String hello() {
		return "hi!!!!";
	}

}

We can glue together interface and implementation via our conf/Module.java:

public class Module extends AbstractModule {

    @Override
    protected void configure() {       

        bind(GreetingService.class).to(GreetingServiceImpl.class);

    }

}

And if you want to use that GreetingService inside your application you can simply use the @Inject annotation:

public class ApplicationController {

    @Inject
    GreetingService greeter;

    public Result injection(Context context) {

        return Results.html().render("greeting", greeter.hello());

    }
}

That's all. No more new statements in your code! That way you only deal with interfaces in your code and you get the ability to change the real implementation. You can easily replace real service implementations with mocked service implementations when developing your application. And it also gives you the ability to run clean mocked tests using Mockito.

Advanced configuration

Ninja includes many default bindings in guice before your application module conf.Module is called. These include bindings for base Ninja features like logging, lifecycle support, scheduler support, etc. There are also bindings for “classic” Ninja features such as freemarker templates, jackson json/xml support, cache, postoffice, and JPA.

You can exclude Ninja's “classic” bindings by extending your conf.Module from ninja.conf.FrameworkModule rather than com.google.inject.AbstractModule. This instructs Ninja to not load ninja.conf.NinjaClassicModule by default. You may find customizing ninja.conf.NinjaClassicModule useful in building the exact set of features you'd like, especially since it has a builder-syntax for enabling/disabling of feature sets.

package conf;

import ninja.conf.FrameworkModule;
import ninja.conf.NinjaClassicModule;
import ninja.utils.NinjaProperties;

public class Module extends FrameworkModule {

    private final NinjaProperties ninjaProperties;

    public Module(NinjaProperties ninjaProperties) {
        this.ninjaProperties = ninjaProperties;
    }
    
    @Override
    protected void configure() {
        // classic ninja stack but with no freemarker or xml
        install(new NinjaClassicModule(ninjaProperties)
            .freemarker(false)
            .xml(false)
        );
    }

}

Conclusion

Dependency injection is the key component to develop and maintain a clean and large codebase in Java. Dependency injection is at the core of Ninja and implemented via Google Guice.

The entry point for your application is the module conf/Module.java.

Guice can do a lot more for you. Read more about the abilities here: https://github.com/google/guice .