Dropwizard Dependency Injection

Dropwizard provides you with simple dependency injection mechanism, using HK2, out-of-the-box, and you can add support for more advanced DI by using Guice bundle.

Dependency Injection Using HK2

The underlying library for out-of-the-box dependency injection mechanism in Dropwizard is Eclipse’s HK2, a CDI-compliant dependency injection framework.

To create a dependency injection configuration that can be overriden during test execution for mocking purposes, put it into your app Configuration for bundle to consume:

public interface DependencyInjectionConfiguration {
    List<Class<?>> getSingletons();
    List<NamedProperty<? extends Object>> getNamedProperties();
}

public class NamedProperty<T> {
    private final String id;
    private final T value;
    private final Class<T> clazz;

    @JsonCreator
    public NamedProperty(@JsonProperty("id") String id, @JsonProperty("value") T value, @JsonProperty("clazz") Class<T> clazz) {
        this.id = id;
        this.value = value;
        this.clazz = clazz;
    }

    public String getId() {
        return id;
    }

    public T getValue() {
        return value;
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class ExampleConfiguration extends Configuration implements DependencyInjectionConfiguration {

    protected Class<?> getUserRepository() {
        return UserRepository.class;
    }

    @Override
    public List<Class<?>> getSingletons() {
        final List<Class<?>> result = new ArrayList();
        result.add(getUserRepository());
        result.add(UserResource.class);

        return result;
    }

    @Override
    public List<NamedProperty<? extends Object>> getNamedProperties() {
        final List<NamedProperty<? extends Object>> result = new ArrayList<>();
        result.add(new NamedProperty<>("dbUser", "dummy_db_user", String.class));

        return result;
    }
}

Then implement a bundle for DI:

public class DependencyInjectionBundle implements ConfiguredBundle<DependencyInjectionConfiguration> {

    @Override
    public void run(DependencyInjectionConfiguration configuration, Environment environment) throws Exception {
            environment
                .jersey()
                .register(
                    new AbstractBinder() {
                        @Override
                        protected void configure() {
                            for (Class<?> singletonClass : configuration.getSingletons()) {
                                bindAsContract(singletonClass).in(Singleton.class);
                            }

                            for (NamedProperty<? extends Object> namedProperty : configuration.getNamedProperties()) {
                                bind((Object) namedProperty.getValue()).to((Class<Object>) namedProperty.getClazz()).named(namedProperty.getId());
                            }
                        }
                    }
                );
    }
}

Then, in your application’s run method, create a new DependencyInjectionBundle:

@Override
public void run(ExampleConfiguration config,
                Environment environment) {
    final DependencyInjectionBundle dependencyInjectionBundle = new DependencyInjectionBundle();
    dependencyInjectionBundle.run(configuration, environment);
}

This allows you to use CDI annotations to control your dependency injection:

@Singleton
public class UserResource {
    private final UserRepository userRepository;

    @Inject
    public UserResource(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

@Singleton
public class UserRepository {
    private final String dbUser;

    @Inject
    public UserRepository(@Named("dbUser") String dbUser) {
        this.dbUser = dbUser;
    }
}

Then you can provide alternate configuration for testing purposes:

public class TestConfiguration extends ExampleConfiguration {

    @Override
    protected Class<?> getUserRepository() {
        return MockUserRepository.class;
    }
}

@DisplayName("User endpoint")
@ExtendWith(DropwizardExtensionsSupport.class)
public class UserControllerTests {
    public static final DropwizardAppExtension<TestConfiguration> app = new DropwizardAppExtension<>(ExampleApplication.class, new TestConfiguration());
}

Note: the @Singleton annotation is only effective for Dropwizard resources. For custom classes, don’t forget to register them as shown above with bindAsContract(singletonClass).in(Singleton.class).