I have a simple REST API project using Jersey 2.x. I tried using Google Guice to inject my dependencies, but it doesn't seem to work. I get this error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=AccountsResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1658198405)
I have this simple resource class
@Path("/accounts")
@Produces(MediaType.APPLICATION_JSON)
public class AccountsResource {
private final AccountService accountService;
@Inject
public AccountsResource(AccountService accountService) {
this.accountService = accountService;
}
@GET
@Path("test")
public String test() {
return this.accountService.test();
}
I want to inject this service into my resource class
public class AccountService {
public AccountService() {}
public String test() {
return "test";
}
}
So, following Guice's guide, I created this module class
import com.google.inject.*;
public class AccountsResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
}
}
Finally, I added the injector in my main method
public class TradingServer implements Runnable {
private static final int PORT = 8181;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AccountsResourceModule());
AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
new TradingServer().run();
}
public void run() {
Server server = new Server(PORT);
ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
ServletContainer container = new ServletContainer(packageConfig);
ServletHolder servletHolder = new ServletHolder(container);
contextHandler.addServlet(servletHolder, "/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
When I call my server, I get the error mentioned above. It seems like the dependency injection didn't work. Please help
So Jersey knows nothing about Guice. It already uses it's own DI framework, HK2. There are a couple things you can do. You can either tie Guice together with HK2 so that HK2 can find services that are bound inside Guice, or another way is to just bind your resource classes inside Guice and and register instances of those resources with Jersey.
Tie Guice with HK2
To tie Guice with HK2, you need to use the Guice HK2 Bridge. First you need to add the following dependency
To get the
hk2.version
look at your Jersey dependencies (you can runmvn dependency:tree
and see what version of HK2 Jersey is pulling in). You want to make sure you are using the exact same version.Next thing you need to do is to programmatically link the two systems. One way to do this is inside a
Feature
.Then just register the feature with Jersey.
And that's it. It should work, as I have tested.
Bind resources with Guice
With the above configuration, Jersey will be creating instances of your resource classes (
@Path
annotated classes). The reason we need the bridge is that Jersey is tightly coupled with HK2, so when we inject our resources classes, when creating the instance, Jersey will call HK2 to try to find all the dependencies for the resource.In this case though, we will not rely on Jersey to create the instance of the resource. We will bind the resource to Guice and let Guice create the instance when we request it. We will use that instance to register with Jersey.
First bind the resource
Also make sure that the
@Inject
annotation in the resource class iscom.google.inject.Inject
.Get instance of resource and register it
You probably have to figure out a cleaner way to do this as you don't want to have to do this for every resource you have. But this is the gist if what you need to do.
Update
So here's a quick implementation to clean up the second solution. What we can do is scan a package recursively to get all the
@Path
annotated classes and then bind them in Guice and register them with Jersey.From this SO post, we can use the Reflections library to easily get all the classes. Just add the following dependency
Then make a little helper classes
Then in your Guice module
And your resource registration