Han*_*ank 6 java dependency-injection jersey grizzly hk2
跟进Jersey + HK2 + Grizzly:注入EntityManager的正确方法?,我想了解如何在不是球衣资源的类中使用依赖注入.
例如,我可能在ExecutorService中运行后台任务,他们可能需要一个EntityManager.如果我尝试@Inject将EntityManager引入该类,则不会发生任何事情.将它注入一个@Path注释的泽西资源类,注入工作正常.
应用程序作为独立的JVM运行,而不是在Java EE应用程序服务器上运行.
更新:我创建了一个测试场景来证明我的意思.代码运行的是一个带有Jersey资源的独立Grizzly服务器,以及一个ExecutorService.A Callable被提交给ExecutorService.
将EntityManager注入资源可以工作,但不能注入Callable.那里的EntityManager仍然存在null.
请告知这里的代码是否比github更好.
因此,要真正了解HK2如何运作,您应该熟悉它ServiceLocator.它类似于Spring ApplicationContext,它是DI框架的主要容器.
在一个独立的应用程序中,您可以简单地通过执行来引导DI容器
ServiceLocatorFactory locatorFactory = ServiceLocatorFactory.getInstance();
ServiceLocator serviceLocator = locatorFactory.create("TestLocator");
ServiceLocatorUtilities.bind(serviceLocator, new EntityManagerProvider());
Run Code Online (Sandbox Code Playgroud)
现在您EntityManagerProvider已注册到容器中.你可以EntityManager简单地查找
EntityManager em = serviceLocator.getService(EntityManager.class);
Run Code Online (Sandbox Code Playgroud)
现在,为了能够利用容器的注入,服务需要由容器管理.比如说你有这个
public class BackgroundTask implements Callable<String> {
@Inject
EntityManager em;
@Override
public String call() throws Exception {
...
}
Run Code Online (Sandbox Code Playgroud)
你实际上做了什么.问题是,BackgroundTask它不是由容器管理的.因此,即使在独立的引导程序(如上面的三行代码)中,也可以实例化任务
BackgroundTask task = new BackgroundTask();
Run Code Online (Sandbox Code Playgroud)
什么都不做,就注射,作为任务类不是由容器管理,并且要创建它自己.如果您想管理它,可以通过几种方法将其注册到容器中.你已经发现了一个(使用一个AbstractBinder)并将绑定器注册到ServiceLocator.然后,不要自己实例化该类,而只是请求它,就像EntityManager上面的例子一样.
或者您可以简单地明确注入任务,即
BackgroundTask task = new BackgroundTask();
serviceLocator.inject(task);
Run Code Online (Sandbox Code Playgroud)
这样做是因为定位器查找EntityManager并将其注入您的任务.
那么这一切如何适应泽西?Jersey(部分)在运行时处理服务查找和资源注入.这就是为什么它在你的Jersey应用程序中运行的原因.当EntityManager需要时,它会查找服务并将其注入资源实例.
所以接下来的问题是,如果任务在Jersey应用程序范围之外运行,你怎么能注入任务?在大多数情况下,以上所有内容几乎都是它的要点.泽西岛有它自己的ServiceLocator,并且尝试获得它的参考并不容易.我们可以给泽西我们 ServiceLocator,但最终新泽西州仍然创建它的自身定位,将与填充它我们的定位.所以最终仍然会有两个定位器.你可以在下面的重构代码中看到我的意思的一个例子,它检查了中的引用ServiceLocatorFeature.
但是如果你想提供ServiceLocatorJersey,你可以将它传递给Grizzly服务器工厂方法
server = GrizzlyHttpServerFactory.createHttpServer(
URI.create(BASE_URI),
config,
serviceLocator
);
Run Code Online (Sandbox Code Playgroud)
现在你仍然可以在泽西岛外使用你的定位器.老实说,在这种情况下,你可能根本不涉及泽西岛,只是保留自己的定位器,只需注册EntityManagerProvider泽西岛和你的ServiceLocator.除了额外的代码行之外,我认为它并没有太大的区别.在功能上,我没有看到任何变化.
要了解有关HK2的更多信息,我强烈建议您仔细阅读用户指南.您将学习很多关于Jersey的内幕动画,并了解您可以将哪些功能合并到Jersey应用程序中.
以下是您的测试的完整重构.我真的没什么变化.我做的任何改变都在上面讨论过.
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); // will throw exception
System.out.println("EntityManager is not null");
return OK;
}
}
public class ServiceLocatorFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
ServiceLocator jerseyLocator
= org.glassfish.jersey.ServiceLocatorProvider
.getServiceLocator(context);
System.out.println("ServiceLocators are the same: "
+ (jerseyLocator == serviceLocator));
return true;
}
}
@Path("/test")
public static class JerseyResource {
@Inject
EntityManager em;
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response doGet() {
System.out.println("GET request received");
Assert.assertNotNull(em);
System.out.println("EntityManager is not null");
return Response.ok()
.entity(OK)
.build();
}
}
@Before
public void setUp() {
serviceLocator = locatorFactory.create("TestLocator");
ServiceLocatorUtilities.bind(serviceLocator, new EntityManagerProvider());
System.out.println("Setting up");
ResourceConfig config = new ResourceConfig();
config.register(new ServiceLocatorFeature());
//config.register(new EntityManagerProvider());
config.register(JerseyResource.class);
// can't find a better way to register the resource
//config.registerInstances(JerseyResource.class);
server = GrizzlyHttpServerFactory.createHttpServer(
URI.create(BASE_URI),
config, serviceLocator
);
backgroundService = Executors.newSingleThreadScheduledExecutor();
}
@After
public void tearDown() {
System.out.println("Shutting down");
server.shutdownNow();
backgroundService.shutdownNow();
}
@Test
public void testScheduledBackgroundTask() throws Exception {
Assert.assertTrue(server.isStarted());
BackgroundTask task = new BackgroundTask();
serviceLocator.inject(task);
Future<String> f = backgroundService.submit(task);
System.out.println("Background task submitted");
try {
Assert.assertEquals(OK, f.get()); // forces Exception
} catch (ExecutionException | InterruptedException ex) {
System.out.println("Caught exception " + ex.getMessage());
ex.printStackTrace();
Assert.fail();
}
}
@Test
public void testBackgroundTask() throws Exception {
Assert.assertTrue(server.isStarted());
BackgroundTask task = new BackgroundTask();
serviceLocator.inject(task);
System.out.println("Background task instantiated");
Assert.assertEquals(OK, task.call());
}
@Test
public void testResource() {
Assert.assertTrue(server.isStarted());
Client client = ClientBuilder.newClient();
WebTarget target = client.target(BASE_URI);
Response r = target.path("test")
.request()
.get();
Assert.assertEquals(200, r.getStatus());
Assert.assertEquals(OK, r.readEntity(String.class));
}
}
Run Code Online (Sandbox Code Playgroud)
我可能会提到的另一件事是你应该只需要一个EntityManagerFactory申请.创建它是昂贵的,并且每次EntityManager需要时创建一个并不是一个好主意.见一个解决方案在这里.