3

Is there any way to inject dependencies into manually created objects?

public class MyCommand {
    @Inject Repository repository;
}

public Repository {
    @Inject EntityManager em;
}

MyCommand command = new MyCommand();

Repository is properly registered the jersey ResourceConfig and can be injected in objects that are created through the CDI container for example a resource class.

But since I create the Command myself the @Inject annotation gets ignored.

Is there a way to get a registered class beside @Inject and @Context? Something like Application.get(Repository.class)

public class MyCommand {
    Repository repository;

    public MyCommand() {
        repository = Application.get(Repository.class);
    }
}

----- EDIT -----

Thanks to your help and some rethinking I found a solution for my problem.

The first thing is that it's possible to inject the ServiceLocator without any preperation into you objects.

The second thing is that I moved from normal commands with a execute method to a a command bus system. The reason for that is I have no controle over the creation of commands so there clean way to get dependencies injected.

The new approach looks like this:

class CommandBus {
    private final ServiceLocator serviceLocator;

    @Inject
    public CommandBus(ServiceLocator serviceLocator) {
        this.serviceLocator = serviceLocator;
    }

    public void dispatch(Command command) {
        Class handlerClass = findHandlerClassForCommand(command);
        CommandHandler handler = (CommandHandler) serviceLocator.getService(handlerClass);
        handler.handle(command);
    }
}

interface CommandHandler {
    void handle(Command command);
}

interface Command {
}

class ConcreteCommand implements Command {
    // I'm just a dto with getters and setters
}

class ConcreteHandler implements CommandHandler {
    private final SomeDependency dependency;

    @Inject
    public ConcreteHandler(SomeDependency dependency) {
        this.dependency = dependency;
    }
    @Override
    public void handle(ConcreteCommand command) {
        // do some things
    }
}

And in my resources I have something like this:

@Path("/some-resource")
class Resource {

    @Context
    private CommandBus bus;

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void runCommand(ConcreteCommand command) {
        bus.dispatch(command);
    }
}
Fleigm
  • 53
  • 1
  • 4
  • This is possible, however i would recommend using a bridge to a general purpose injection framework (e.g. google guice). Alternatively jersey injection and binding is described in my answer here: http://stackoverflow.com/questions/38976724/how-to-set-an-object-to-context-so-that-i-can-get-it-anywhere-in-the-application/39000866#39000866 – pandaadb Sep 20 '16 at 12:55
  • I would claim that hk2 is a general purpose injection framework ;-) – jwells131313 Sep 20 '16 at 15:49
  • @jwells131313 you are absolutely right :) – pandaadb Sep 21 '16 at 09:12

2 Answers2

2

As pointed out by jwells - HK2 is an injection framework :)

I spent some time looking into it - I have to say, I find it much more complicated than say guice or spring. Maybe this is due to the fact that I use Dropwizard and it makes it not as easy to access the Service locators.

However, here is how you can do that.

First, you will have to get a reference to your ServiceLocator. It must be the same ServiceLocator that jersey is using as well. You can access it for example like:

How to get HK2 ServiceLocator in Jersey 2.12?

In my example code I will use an event listener, which is due to my Dropwizard Setup.

You now have 2 choices: Register your command with your Service Locator and have the injection framework handle creation, or pass the ServiceLocator to your command in order to use it.

I wrote up a quick example using Dropwizard and jersey:

public class ViewApplication extends io.dropwizard.Application<Configuration> {

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {

        environment.jersey().register(new ApplicationEventListener() {
            @Override
            public void onEvent(ApplicationEvent event) {
                if (event.getType() == ApplicationEvent.Type.INITIALIZATION_FINISHED) {
                    ServiceLocator serviceLocator = ((ServletContainer) environment.getJerseyServletContainer())
                            .getApplicationHandler().getServiceLocator();

                    ServiceLocatorUtilities.bind(serviceLocator, new AbstractBinder() {

                        @Override
                        protected void configure() {
                            bind(new Repository("test")).to(Repository.class);
                            bind(MyCommandInjected.class).to(MyCommandInjected.class);
                        }
                    });

                    MyCommandInjected service = serviceLocator.getService(MyCommandInjected.class);
                    MyCommandManual tmp = new MyCommandManual(serviceLocator);
                }
            }
            @Override
            public RequestEventListener onRequest(RequestEvent requestEvent) {
                return null;
            }
        });


    }

    @Override
    public void initialize(Bootstrap<Configuration> bootstrap) {
        super.initialize(bootstrap);
    }

    public static void main(String[] args) throws Exception {
        new ViewApplication().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
    }

    @Path("test")
    @Produces(MediaType.APPLICATION_JSON)
    public static class HelloResource {

        @GET
        @Path("asd")
        public String test(String x) {
            return "Hello";
        }

    }

    public static class Repository {

        @Inject
        public Repository(String something) {
        }
    }

    public static class MyCommandInjected {

        @Inject
        public MyCommandInjected(final Repository repo) {
            System.out.println("Repo injected " + repo);
        }
    }

    public static class MyCommandManual {

        public MyCommandManual(final ServiceLocator sl) {
            Repository service = sl.getService(Repository.class);
            System.out.println("Repo found: " + service);
        }
    }

}

In the Run method, i get access to my ServiceLocator. I bind my classes in there (so there is an example of how to do that). You can alternatively also register Binders with jersey directly - they will use the correct ServiceLocator.

The 2 classes MyCommandInjected and MyCommandManual are examples of how you can create this command.

The relevant line for you is probably:

Repository service = sl.getService(Repository.class);

This asks the service locator for a new instance of the Repository.

Now, this is just a quick example. I am much more fond of the guice bridge than using HK2 directly :) I find it much easier to use and much clearer. Using the guice-jersey-bridge you can do everything through guice and it will automatically do the right thing.

Hope that brings some inside,

Artur

Community
  • 1
  • 1
pandaadb
  • 6,306
  • 2
  • 22
  • 41
0

You can use the inject method of ServiceLocator in order to inject already created objects. ServiceLocator is the basic registry of HK2 and should be available in your resource.

jwells131313
  • 2,364
  • 1
  • 16
  • 26