简体   繁体   中英

Jersey HK2 injection in manually created objects

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 aa 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);
    }
}

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM