Search code examples
javagradledependency-injectionvert.xhk2

Vertx & HK2 - Launching the application (dependency injection)


I'm trying to build an application with Vert.x and HK2 extension for dependency injection. I however cannot seem to find any examples that show me the full picture.

Note that I am completely new to dependency injection.

I did as shown in this example, however I am getting a NoSuchMethodException when starting the application because it tries to access a default, parameterless constructor in the verticle class (SimpleVerticle), which does not exist.

In my build.gradle, the mainClassName is set to 'io.vertx.core.Launcher', and in the shadowJar manifest attributes, "Main-Verticle" is set to the SimpleVerticle as seen in the example.

Surely I'm missing something somewhere. Could anyone show me what I'm missing or point me to some up-to-date, full examples?

  • Vert.x version: 3.4.2
  • Vert.x HK2 version: 2.5.0

build.gradle:

buildscript {
    repositories {
        mavenCentral()
    }
}

plugins {
    id 'java'
    id 'application'
    id 'com.github.johnrengelman.shadow' version '1.2.3'
    id 'java-library'
    id 'eclipse'
    id 'idea'
    id 'maven'
    id 'groovy'
    id 'jacoco'
}


group 'example'
version "${buildVersion}"

repositories {
    mavenCentral()
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

dependencies {
    compile('io.vertx:vertx-core:3.4.2')
    compile('io.vertx:vertx-web:3.4.2')
    compile('javax.json:javax.json-api:1.1')
    compile('org.glassfish:javax.json:1.0.4')
    compile('log4j:log4j:1.2.17')
    compile('io.vertx:vertx-web-client:3.4.2')
    compile('com.englishtown.vertx:vertx-hk2:2.5.0')
    testCompile "junit:junit:4.12"
    testCompile "io.vertx:vertx-unit:3.3.3"
    testCompile "com.jayway.restassured:rest-assured:2.4.0"
    testImplementation 'junit:junit:4.12'
}

mainClassName = 'io.vertx.core.Launcher'

shadowJar {
    classifier = 'fat'
    baseName = 'aggregator-api'
    manifest {
        attributes "Main-Verticle": 'example.startup.StartupVerticle'
    }
    mergeServiceFiles {
        include 'META-INF/services/io.vertx.core.spi.VerticleFactory'
    }
}

StartupVerticle:

package example.startup;

import example.config.ConfigReader;
import io.vertx.core.AbstractVerticle;

import javax.inject.Inject;

public class StartupVerticle extends AbstractVerticle {

    private final ConfigReader configReader;

    @Inject
    public StartupVerticle(final ConfigReader configReader) {
        this.configReader = configReader;
    }

    @Override
    public void start() throws Exception {
        if(this.configReader == null) throw new IllegalStateException("ConfigReader was not injected!");

        super.start();
        System.out.println("Started verticle using DI");
    }

}

ConfigBinder:

package example.binder;

import example.config.ConfigReader;
import example.config.ConfigReaderImpl;
import org.glassfish.hk2.utilities.binding.AbstractBinder;

public class ConfigBinder extends AbstractBinder {

    @Override
    protected void configure() {
        this.bind(ConfigReaderImpl.class).to(ConfigReader.class);
    }

}

ConfigReader:

package example.config;

import io.vertx.core.json.JsonObject;

public interface ConfigReader {

    JsonObject getConfig();

}

ConfigReaderImpl:

package example.config;

import io.vertx.core.json.JsonObject;

public class ConfigReaderImpl implements ConfigReader {

    private final JsonObject config;

    ConfigReaderImpl(JsonObject config) {
        this.config = config;
    }

    @Override
    public JsonObject getConfig() {
        return this.config;
    }

}

Solution

  • Managed to fix it.

    I switched to Guice I was missing some stuff for my JsonObject injection for the ConfigReaderImpl class

    New ConfigBinder:

    public class ConfigBinder extends AbstractModule {
    
        private final String CONFIG_PATH = "config";
    
        @Override
        protected void configure() {
            this.bind(ConfigReader.class).to(ConfigReaderImpl.class).in(Scopes.NO_SCOPE);
            this.bindConfig(Vertx.currentContext().config(), this.CONFIG_PATH);
        }
    
        private void bindConfig(JsonObject config, String path) {
            this.bind(JsonObject.class).annotatedWith(Names.named(path)).toInstance(config);
        }
    
    }
    

    I was also missing an annotation in the ConfigReaderImpl class:

    public class ConfigReaderImpl implements ConfigReader {
    
        private final JsonObject config;
    
        @Inject
        private ConfigReaderImpl(@Named("config") final JsonObject config) {
            this.config = config;
        }
    
        @Override
        public JsonObject getConfig() {
            return this.config;
        }
    
    }
    

    And I managed to deploy a dependency injected Verticle with:

    Injector injector = Guice.createInjector(new ConfigBinder());
    Verticle verticle = injector.getInstance(SomeVerticle.class);
    this.getVertx().deployVerticle(verticle);