Following up on Jersey + HK2 + Grizzly: Proper way to inject EntityManager?, I would like to understand how it is possible use dependency injection in classes which are not jersey resources.
As an example, I might have background tasks running in an ExecutorService, and they might need an EntityManager. If I attempt to @Inject
the EntityManager into the class, nothing happens. Injecting it into a @Path
-annotated jersey resource class, injecting works fine.
The application is running as a standalone JVM, not on a Java EE application server.
Update: I have created a test scenario to demonstrate what I mean. The code is running a standalone Grizzly server with a Jersey resource, as well as an ExecutorService. A Callable
is submitted to the ExecutorService.
Injection of the EntityManager into the resource works, but not into the Callable. There the EntityManager remains null
.
Please advise if the code is better kept here than on github.
So to really understand how HK2 works, you should become familiar with its
ServiceLocator
. It is analogous to SpringApplicationContext
, which is the main container for the DI framework.In a standalone app, you could bootstrap the DI container simply by doing
Now your
EntityManagerProvider
is registered into the container. You can lookup theEntityManager
simply by doingNow in order to be able to take advantage of injection by the container, the service needs to be managed by the container. For example say you have this
which you actually do. The problem is, the
BackgroundTask
is not managed by the container. So even in a standalone bootstrap (like the three lines of code above), instantiating the taskdoes nothing, as far as injection, as the task class is not managed by the container, and you are creating it yourself. If you wanted it managed, there a few ways to register it to the container. You've discovered one already (use an
AbstractBinder
) and register the binder to theServiceLocator
. Then instead of instantiating the class yourself, you just request it, like theEntityManager
example above.Or you can simply explicitly inject the task, i.e
What that did was cause the locator to lookup the
EntityManager
and inject it into your task.So how does this all fit in with Jersey? Jersey (partly) handles lookup of services and injection into resources during it's runtime. That's why it work's in your Jersey application. When the
EntityManager
is needed, it looks up the service an injects it into the resource instance.So the next question is, if the tasks are being run outside the scope the Jersey application, how can you inject the task? For the most part, all the above is pretty much the gist of it. Jersey has it's own
ServiceLocator
, and it's not easy to try a obtain a reference to it. We could give Jersey ourServiceLocator
, but Jersey ultimately still creates it's own locator and will populate it with our locator. So ultimately there would still be two locators. You can see an example of what I mean in the refactored code below, where it check the references in theServiceLocatorFeature
.But if you do want to provide the
ServiceLocator
to Jersey, you can pass it to the Grizzly server factory methodNow you can still use your locator outside of Jersey. Honestly though, in this case, you could not involve Jersey at all and just keep your own locator, and just register the
EntityManagerProvider
with both Jersey and yourServiceLocator
. I don't see it really making much difference, except for the extra line of code. Functionally, I don't see any change.To learn more about HK2, I highly recommend thoroughly going through the user guide. You'll learn a lot about what goes on under the hood with Jersey, and also learn about features that you can incorporate into a Jersey application.
Below is the complete refactor of your test. I didn't really change much. Any changes I made are pretty much discussed above.
Another thing I might mention is that you should need only one
EntityManagerFactory
for the application. It's expensive to create, and creating one every time theEntityManager
is needed is not a good idea. See one solution here.Statement: Implementation of Dependency Injection using Grizzly and Jersey
Please follow the below steps to do the same –
List item Create a class called Hk2Feature which implements Feature -
List item Create a class called MyAppBinder which extends AbstractBinder and you need to register all the services here like below –
List item Now, it’s time to write your own services and inject all the required services in your appropriate controllers like below code – package com.sample.di;
Now hit the url http://localhost:8080/main?name=Tanuj and you will get your result. This is how you can achieve dependency injection in Grizzly Jersey application. Find the detailed implementation of the above skeleton in my repo. Happy Coding