To really understand how HK2 works, you should familiarize yourself with its ServiceLocator . It is similar to Spring ApplicationContext , which is the main container for the DI framework.
In a standalone application, you can load the DI container simply by doing
ServiceLocatorFactory locatorFactory = ServiceLocatorFactory.getInstance(); ServiceLocator serviceLocator = locatorFactory.create("TestLocator"); ServiceLocatorUtilities.bind(serviceLocator, new EntityManagerProvider());
Now your EntityManagerProvider registered in the container. You can find EntityManager simply by doing
EntityManager em = serviceLocator.getService(EntityManager.class);
Now, in order to be able to use container injections, maintenance must be managed by the container. For example, let's say you have this
public class BackgroundTask implements Callable<String> { @Inject EntityManager em; @Override public String call() throws Exception { ... }
which you are actually doing. The problem is that the BackgroundTask container is not managed by the container. Therefore, even in an autonomous bootstrap (for example, in the three lines of code above), creating an instance of the task
BackgroundTask task = new BackgroundTask();
does nothing, since the injection, since the task class is not controlled by the container, and you yourself create it. If you want this to succeed, there are several ways to register it in a container. You have already opened it (use AbstractBinder ) and register the binding to the ServiceLocator . Then, instead of instantiating the class, you simply request it, as the EntityManager example above.
Or you can just explicitly enter the task, i.e.
BackgroundTask task = new BackgroundTask(); serviceLocator.inject(task);
What this did was make the locator look for the EntityManager and paste it into your task.
So how does it all fit in Jersey? Jersey (partially) handles the search for services and the injection of resources at run time. That is why it works in your application for Jersey. When an EntityManager is required, it searches for a service that injects it into the resource instance.
So, the next question: if tasks run outside the Jersey application, how can you enter a task? For the most part, all of the above is largely its essence. Jersey has its own ServiceLocator , and itβs not easy to try to get a link to it. We could give Jersey our ServiceLocator , but Jersey ultimately still creates its own locator and populates it with our locator. Thus, in the end, there will still be two locators. You can see an example of what I mean in the updated code below, where it checks the links in ServiceLocatorFeature .
But if you want to provide a ServiceLocator Jersey, you can pass it to the Grizzly server factory method
server = GrizzlyHttpServerFactory.createHttpServer( URI.create(BASE_URI), config, serviceLocator );
Now you can use your locator outside of Jersey. Honestly, in this case you would not be able to attract Jersey at all and just save your locator and just register EntityManagerProvider with both Jersey and your ServiceLocator . I don't see this really make a big difference, except for an extra line of code. Functionally, I do not see any changes.
To learn more about HK2, I highly recommend that you carefully read the user manual . You will learn a lot about what is happening under the hood with a Jersey, and also learn about the features that you can include in the Jersey app.
Below is a complete refactoring of your test. I have not changed much. Any changes I made are pretty much discussed above.
public class DependencyInjectionTest { private final ServiceLocatorFactory locatorFactory = ServiceLocatorFactory.getInstance(); private ServiceLocator serviceLocator; private final static String BASE_URI = "http://localhost:8888/"; private final static String OK = "OK"; private HttpServer server; private ExecutorService backgroundService; public class EntityManagerProvider extends AbstractBinder implements Factory<EntityManager> { private final EntityManagerFactory emf; public EntityManagerProvider() { emf = Persistence.createEntityManagerFactory("derbypu"); } @Override protected void configure() { bindFactory(this).to(EntityManager.class); System.out.println("EntityManager binding done"); } @Override public EntityManager provide() { EntityManager em = emf.createEntityManager(); System.out.println("New EntityManager created"); return em; } @Override public void dispose(EntityManager em) { em.close(); } } public class BackgroundTask implements Callable<String> { @Inject EntityManager em; @Override public String call() throws Exception { System.out.println("Background task started"); Assert.assertNotNull(em);
Another thing I could mention is that for an application you only need one EntityManagerFactory . It's expensive to create, and every time you need an EntityManager , creating one is not recommended. See One solution here .