jersey + grizzly + hk2:依赖注入,但不是资源

时间:2015-08-11 13:27:55

标签: java dependency-injection jersey grizzly hk2

关注Jersey + HK2 + Grizzly: Proper way to inject EntityManager?,我想了解如何在 not jersey resources 的类中使用依赖注入。

作为一个例子,我可能在ExecutorService中运行后台任务,他们可能需要一个EntityManager。如果我尝试将@Inject EntityManager放入类中,则不会发生任何事情。将它注入@Path - 注释的泽西资源类,注入工作正常。

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

更新:我创建了一个test scenario来证明我的意思。代码运行的是一个带有Jersey资源的独立Grizzly服务器,以及一个ExecutorService。 Callable已提交给ExecutorService。

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

请告知此处的代码是否比github更好。

2 个答案:

答案 0 :(得分:8)

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

在独立应用程序中,只需执行

即可引导DI容器
ServiceLocatorFactory locatorFactory = ServiceLocatorFactory.getInstance();
ServiceLocator serviceLocator = locatorFactory.create("TestLocator");
ServiceLocatorUtilities.bind(serviceLocator, new EntityManagerProvider());

现在您的EntityManagerProvider已注册到容器中。您只需执行

即可查找EntityManager
EntityManager em = serviceLocator.getService(EntityManager.class);

现在,为了能够利用容器的注入,服务需要由容器管理。比如说你有这个

public class BackgroundTask implements Callable<String> {

    @Inject
    EntityManager em;

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

你实际上做的。问题是,BackgroundTask不是由容器管理的。因此,即使在独立的引导程序(如上面的三行代码)中,也可以实例化任务

BackgroundTask task = new BackgroundTask();
就注入而言,

什么都不做,因为任务类不是由容器管理的,是你自己创建的。如果您想管理它,可以通过几种方法将其注册到容器中。您已经发现了一个(使用AbstractBinder)并将绑定器注册到ServiceLocator。然后,您只需自己实例化该类,而不是像上面的EntityManager示例那样请求它。

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

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

这样做会导致定位器查找EntityManager并将其注入您的任务。

那么这一切如何适应泽西? Jersey(部分)在运行时处理服务查找和资源注入。这就是为什么它在你的Jersey应用程序中运行的原因。当需要EntityManager时,它会查找服务并将其注入资源实例。

接下来的问题是,如果任务在Jersey应用程序范围之外运行,你如何注入任务?在大多数情况下,以上所有内容几乎都是它的要点。泽西岛有它自己的ServiceLocator,并且尝试获取它的引用并不容易。我们可以给Jersey 我们的 ServiceLocator,但Jersey最终仍会创建它的自己的定位器,并用我们的定位器填充它。所以最终仍然会有两个定位器。您可以在下面的重构代码中看到我的意思示例,它会检查ServiceLocatorFeature中的引用。

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

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

现在你仍然可以在泽西岛外使用你的定位器。老实说,在这种情况下,你可以完全涉及泽西岛,只保留你自己的定位器,只需在泽西岛和你的EntityManagerProvider注册ServiceLocator即可。除了额外的代码行之外,我认为它并没有太大的区别。在功能上,我没有看到任何变化。

要了解有关HK2的更多信息,我强烈建议您仔细阅读user guide。您将学习很多关于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));
    }
}

我可能会提到的另一件事是你应该只需要一个EntityManagerFactory来申请。创建成本很高,每次需要EntityManager时创建一个并不是一个好主意。请参阅一个解决方案here

答案 1 :(得分:0)

声明:使用Grizzly和Jersey

实施依赖注入

请按照以下步骤进行相同操作 -

  • 列表项创建一个名为Hk2Feature的类,它实现了功能 -

    package com.sample.di;
    import javax.ws.rs.core.Feature;
    import javax.ws.rs.core.FeatureContext;
    import javax.ws.rs.ext.Provider;
    @Provider
    public class Hk2Feature implements Feature {
      public boolean configure(FeatureContext context) {
        context.register(new MyAppBinder());
        return true;
      }
    }
    
  • 列表项创建一个名为MyAppBinder的类,它扩展了AbstractBinder,你需要在这里注册所有服务 -

    package com.sample.di;
    import org.glassfish.hk2.utilities.binding.AbstractBinder;
    public class MyAppBinder extends AbstractBinder {
      @Override
      protected void configure() {
        bind(MainService.class).to(MainService.class);
      }
    }
    
  • 列表项现在,是时候编写自己的服务并在适当的控制器中注入所有必需的服务,如下面的代码 - 包com.sample.di;

    public class MainService {
      public String testService(String name) {
        return “Hi” + name + “..Testing Dependency Injection using Grizlly Jersey “;
      }
    }
    package com.sample.di;
    import javax.inject.Inject;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.core.MediaType;
    @Path(“/main”)
    public class MainController {
        @Inject
        public MainService mainService;
        @GET
        public String get(@QueryParam(“name”) String name) {
            return mainService.testService(name);
        }
        @GET
        @Path(“/test”)
        @Produces(MediaType.APPLICATION_JSON)
        public String ping() {
            return “OK”;
        }
    }
    

现在点击网址http://localhost:8080/main?name=Tanuj即可获得结果。这就是你如何在Grizzly Jersey应用程序中实现依赖注入。在我的repo中找到上述骨架的详细实现。快乐编码