Resteasy and Google Guice: how to use multiple @Ap

2019-06-17 05:42发布

问题:

I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.

My intentions are:

  • Use multiple @ApplicationPath for the versions of my API. In each class annotated with @ApplicationPath I load a set of classes for the specific version.
  • Each resource have a @Inject (from Google Guice) in his constructor to inject some services.

I created two classes annotated with @ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.

My Module is configured like this:

public class HelloModule implements Module
{
   public void configure(final Binder binder)
   {
      binder.bind(IGreeterService.class).to(GreeterService.class);

      binder.bind(IUserService.class).to(UserService.class);
   }
}

When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:

java.lang.RuntimeException: RESTEASY003190: Could not find constructor 
    for class: org.jboss.resteasy.examples.guice.hello.HelloResource

Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.

But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.

If I abandon the idea of use @ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:

public class HelloModule implements Module
{
   public void configure(final Binder binder)
   {
      binder.bind(HelloResource.class);
      binder.bind(IGreeterService.class).to(GreeterService.class);

      binder.bind(UserResource.class);
      binder.bind(IUserService.class).to(UserService.class);
   }
}

But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple @ApplicationPath.

So, what I'm missing or not understanding?

I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md

Thanks!

回答1:

When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection. And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.

Below are the changes that I made to make it work the way you want.

ApplicationV1RS.java

@ApplicationPath("v1")
public class ApplicationV1RS extends Application {

    private Set<Object> singletons = new HashSet<Object>();

    public ApplicationV1RS(@Context ServletContext servletContext) {
    }

    @Override
    public Set<Object> getSingletons() {
        Injector injector = Guice.createInjector(new HelloModule());

        HelloResource helloResource = injector.getInstance(HelloResource.class);
        UserResource userResource = injector.getInstance(UserResource.class);
        singletons.add(helloResource);
        singletons.add(userResource);
        return singletons;
    }
}

ApplicationV2RS.java

@ApplicationPath("v2")
public class ApplicationV2RS extends Application {

    private Set<Object> singletons = new HashSet<Object>();

    public ApplicationV2RS(@Context ServletContext servletContext) {
    }

    @Override
    public Set<Object> getSingletons() {
        Injector injector = Guice.createInjector(new HelloModule());

        HelloResource helloResource = injector.getInstance(HelloResource.class);
        UserResource userResource = injector.getInstance(UserResource.class);
        singletons.add(helloResource);
        singletons.add(userResource);
        return singletons;
    }
}

HelloResource.java

@Path("hello")
public class HelloResource {
    @Inject
    private IGreeterService greeter;

    public HelloResource() {
    }

    @GET
    @Path("{name}")
    public String hello(@PathParam("name") final String name) {
        return greeter.greet(name);
    }
}

UserResource.java

@Path("user")
public class UserResource {

    @Inject
    private IUserService userService;

    public UserResource() {
    }

    @GET
    @Path("{name}")
    public String hello(@PathParam("name") final String name) {
        return userService.getUser(name);
    }
}

Add @Singleton to your Service Classes.

Hope it helps.

I have also pushed the code to forked repo. check it out