user3303372 user3303372 - 28 days ago 5
Java Question

How to Disable Dependency Injection in Jersey 2?

I'm new to creating REST web services with Jersey 2. I was able to get a few simple cases going now I'm moving on to "real" work.

I create my own instances of

ResourceConfig
and populate the
ResourceConfig
instances with instances of the REST controller returned by Guice:

<!-- language: lang-java -->
final ResourceConfig rc = new ResourceConfig();

//register an *instance* of a REST controller
rc.register(injector.getInstance(MyRestController.class));


Note that since these REST controller instances were provided by Guice, the instances are populated with all necessary dependencies that are marked with the
@Inject
annotation.

However, when I run the above code to bootstrap Jersey, I see an error like the following:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(....


Apparently what is happening is that Jersey is also trying to resolve the
@Inject
dependencies on the REST controller instance, because it sees the same
@Inject
annotations that Guice does.

I don't want Jersey to do any dependency injection. I think there are bridges from Jersey to Guice, but based on program requirements that I can't show here, I need to create the REST controller instances myself and ask the Guice
Injector
for them.

My question: How can I disable the dependency injection in Jersey?

I'm currently using the
javax.inject.Inject
annotation. Maybe I can use the
com.google.inject.Inject
annotation instead, thinking that Jersey won't be looking for these annotations. But still, I would rather just turn off dependency injection in Jersey. How can I do that?

Thank you!!

Answer

First, your solution:

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

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        JerseyEnvironment jersey = environment.jersey();

        // create the Guice env and its dependencies
        Injector i = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                Map<String, String> props = new HashMap<>();
                props.put("testme", "Hello World Guice Inject Test");
                Names.bindProperties(binder(), props);
                bind(HelloResource.class).in(Singleton.class);
            }
        });

        // get instance

        HelloResource resourceInstance = i.getInstance(HelloResource.class);
        jersey.register(new AbstractBinder() {

            @Override
            protected void configure() {
                // teach jersey about your guice dependency 
                bind(resourceInstance).to(HelloResource.class);
            }
        });

        jersey.register(HelloResource.class); // register resource - jersey will discover this from the binding
    }

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

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

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


        @Inject
        @Named("testme")
        private String testString;

        public HelloResource() {
            System.err.println("I am created now");
        }

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

    }

}

Please disregard my DropWizard setup. It uses Jersey and it uses Guice, so the registering part is the same.

You are facing 2 dilemmas here:

  1. Jersey DI will attempt to inject fields in your object. That is because it doesn't realise there is an object that is already done. It assumes it has to inject the fields.

So, my solution above does the following:

Bind Guice bean to jersey environment. This will enable jersey to find the bean that you created. Since it is bound into the jersey env, it will not attempt to re-init the bean, but rather treat it as a fully valid object. For this to work, you need to register the Resource as a class argument (triggers jersey to search for a registered bean, or create one if needed)

The other solution you could do is to move your Injects to the Constructor (generally good practice to avoid field injection). Because you register an object, it is illegal to call the constructor again. Therefore jersey will not attempt to do any injection (since there is none to do)

Finally, I don't know how your requirements work, but what you want to do is essentially to manually create a Guice-Jersey Bridge. Teaching your beans to jersey in the way you demonstrate is EXACTLY what the Guice-Jersey bridge does, with the exception that it fixes all those little edge cases (as the one you see now). It is HIGHLY recommended you implement that bridge.

What that bridge does is really to simply delegate creation of beans to guice. That means that it will (1) ask guice for an instance and then create one itself (if Guice returns no instance).

Hiope that helps,

Cheers! Artur