球衣+灰熊+ hk2:依赖注入,但不能进入资源 [英] jersey + grizzly + hk2: Dependency injection, but not into resource

查看:180
本文介绍了球衣+灰熊+ hk2:依赖注入,但不能进入资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关注泽西+ HK2 +灰熊:正确的注入方式EntityManager?,我想了解如何在类中使用依赖注入,而不是泽西资源。

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.

作为一个例子,我可能在ExecutorService中运行后台任务,他们可能需要一个EntityManager。如果我尝试将EntityManager的 @Inject ,没有任何反应。将它注入一个 @Path - 注意工作正常的资源类。

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.

应用程序正在运行独立的JVM,而不是Java EE应用程序服务器。

The application is running as a standalone JVM, not on a Java EE application server.

更新:我创建了一个测试场景来展示我的意思。该代码正在运行带有泽西资源的独立灰熊服务器以及ExecutorService。将可调用提交给ExecutorService。

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.

将EntityManager注入资源工作,但不进入Callable 。 EntityManager保持 null

Injection of the EntityManager into the resource works, but not into the Callable. There the EntityManager remains null.

请指示代码是否比github更好。 >

Please advise if the code is better kept here than on github.

推荐答案

为了真正了解HK2如何工作,您应该熟悉其 ServiceLocator 。它类似于Spring ApplicationContext ,它是DI框架的主要容器。

So to really understand how HK2 works, you should become familiar with its ServiceLocator. It is analogous to Spring ApplicationContext, which is the main container for the DI framework.

在独立的应用程序中,您可以通过执行

In a standalone app, you could bootstrap the DI container simply by doing

ServiceLocatorFactory locatorFactory = ServiceLocatorFactory.getInstance();
ServiceLocator serviceLocator = locatorFactory.create("TestLocator");
ServiceLocatorUtilities.bind(serviceLocator, new EntityManagerProvider());

现在您的 EntityManagerProvider 已注册到容器。您可以通过执行

Now your EntityManagerProvider is registered into the container. You can lookup the EntityManager simply by doing

EntityManager em = serviceLocator.getService(EntityManager.class);

为了能够利用容器的注入,服务需要管理通过容器例如说你有这个

Now 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

public class BackgroundTask implements Callable<String> {

    @Inject
    EntityManager em;

    @Override
    public String call() throws Exception {
        ...
}

你实际上做的。问题是, BackgroundTask 不是由容器管理的。所以即使在一个独立的bootstrap(如上面的三行代码),实例化任务

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 task

BackgroundTask task = new BackgroundTask();

只要注入,任务类不由容器管理,你自己创建它。如果您想要管理,可以通过几种方式将其注册到容器。您已经发现了一个(使用 AbstractBinder ),并将绑定器注册到 ServiceLocator 。然后,您不需要自己实例化类,就像上面的 EntityManager 示例一样。

does 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 the ServiceLocator. Then instead of instantiating the class yourself, you just request it, like the EntityManager example above.

或者你可以简单明确地注入任务,即

Or you can simply explicitly inject the task, i.e

BackgroundTask task = new BackgroundTask(); 
serviceLocator.inject(task);

这样做是因为定位器查找 EntityManager 并将其注入您的任务。

What that did was cause the locator to lookup the EntityManager and inject it into your task.

那么这一切如何适合泽西?泽西(部分)在运行时处理查询服务和注入资源。这就是为什么它的工作在你的泽西申请。当需要 EntityManager 时,它将查找服务注入资源实例。

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.

所以下一个问题是,如果任务正在运行范围之外,泽西应用程序,你如何注入任务?在大多数情况下,以上所有都是它的要点。泽西有它自己的 ServiceLocator ,尝试获得它的引用是不容易的。我们可以给泽西我们的 ServiceLocator ,但泽西最终仍然创建它的自己的定位器,并将使用我们的定位器。所以最终还是会有两个定位器。您可以在下面的重构代码中看到一个例子,它在哪里检查 ServiceLocatorFeature 中的引用。

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 our ServiceLocator, 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 the ServiceLocatorFeature.

但是如果您想向泽西岛提供 ServiceLocator ,可以将其传递给Grizzly服务器工厂方法

But if you do want to provide the ServiceLocator to Jersey, you can pass it to the Grizzly server factory method

server = GrizzlyHttpServerFactory.createHttpServer(
        URI.create(BASE_URI),
        config, 
        serviceLocator
);

现在,您仍然可以在泽西之外使用您的定位器。老实说,在这种情况下,你可以根本不需要泽西,只要保持你自己的定位器,并且注册 EntityManagerProvider 与泽西和您的 ServiceLocator 。我看不出它真的有很大的区别,除了额外的代码行。在功能上,我没有看到任何变化。

Now 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 your ServiceLocator. I don't see it really making much difference, except for the extra line of code. Functionally, I don't see any change.

要了解更多关于HK2的信息,我强烈建议您彻底了解用户指南。你会学到很多关于泽西岛下的事情,并了解你可以融入泽西州的应用程序的功能。

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.

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));
    }
}

另外我可以提到的是你只需要一个 EntityManagerFactory 为应用程序。每次创建 EntityManager 时,创建一个创建并不是一个好主意。请参阅此处的一个解决方案。

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 the EntityManager is needed is not a good idea. See one solution here.

这篇关于球衣+灰熊+ hk2:依赖注入,但不能进入资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆