6

如果我使用 Jersey 1.12,并且我有多个资源类,并且它们都需要访问一些共享上下文,那么注入依赖项的最佳方法是什么,无论是在资源类的构造函数中还是在处理程序方法中?我需要使用外部 DI 库,还是 Jersey 有内置的东西?

即,Foos 的资源可能如下所示:

package com.example.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;

@Path("/some/api/path/foo")
public class FooResource
{
    @GET
    @Produces("text/html")
    public String getFoo(@QueryParam("id") String id)
    {
        Foo foo = /* get a Foo from some shared context based on id */
        /* Process foo into a String */
    }
}

对于酒吧:

package com.example.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;

@Path("/some/api/path/bar")
public class BarResource
{
    @GET
    @Produces("text/html")
    public String getBar(@QueryParam("id") String id)
    {
        Bar bar = /* get a Bar from some shared context based on id */
        /* Process bar into a String */
    }
}
4

4 回答 4

12

我最终使用了 Google Guice,这是一个与 Jersey 很好地集成的轻量级 DI 框架。这是我必须做的:

首先,我在 pom.xml 中添加了依赖项:

    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>3.0</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey.contribs</groupId>
        <artifactId>jersey-guice</artifactId>
        <version>1.12</version>
        <scope>compile</scope>
    </dependency>

我想要一个 DAO 实现为带有接口的单例:

public interface MySingletonDao
{
    // ... methods go here ...
}

和一个具体的实现:

@Singleton
public class ConcreteMySingletonDao implements MySingletonDao
{
    // ... methods go here ...
}

像这样装饰资源类:

@Path("/some/path")
@RequestScoped
public class MyResource
{
    private final MySingletonDao mySingletonDao;

    @Inject
    public MyResource(MySingletonDao mySingletonDao)
    {
        this.mySingletonDao = mySingletonDao;
    }

    @POST
    @Produces("application/json")
    public String post() throws Exception
    {
            // ... implementation goes here ...
    }
}

创建了一个将执行绑定的类:

public class GuiceConfig extends GuiceServletContextListener
{
    @Override
    protected Injector getInjector()
    {
        return Guice.createInjector(new JerseyServletModule()
        {
            @Override
            protected void configureServlets()
            {
                bind(MyResource.class);
                bind(AnotherResource.class);
                bind(MySingletonDao.class).to(ConcreteMySingletonDao.class);
                serve("/*").with(GuiceContainer.class);
            }
        });
    }
}

我使用 Jetty 而不是 Glassfish 来实际充当服务器。在我的功能测试中,这看起来像:

private void startServer() throws Exception
{
    this.server = new Server(8080);
    ServletContextHandler root =
        new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);

    root.addEventListener(new GuiceConfig());
    root.addFilter(GuiceFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
    root.addServlet(EmptyServlet.class, "/*");

    this.server.start();
}

来自EmptyServletSunny Gleason 的示例代码作为答案给出:https ://stackoverflow.com/a/3296467——我最初有

root.addServlet(new ServletHolder(new ServletContainer(new PackagesResourceConfig("com.example.resource"))), "/*");

而不是线

root.addServlet(EmptyServlet.class, "/*");

但这导致 Jersey 尝试执行依赖注入而不是 Guice,这会导致运行时错误。

于 2012-05-26T06:42:26.810 回答
3

您可以使用 SingletonTypeInjectableProvider:http: //jersey.java.net/nonav/apidocs/1.12/jersey/com/sun/jersey/spi/inject/SingletonTypeInjectableProvider.html

样本:

ResourceConfig resourceConfig = new DefaultResourceConfig();
resourceConfig.getSingletons().add(
        new SingletonTypeInjectableProvider<Context, SingletonType>(
               SingletonType.class, new SingletonType()) {});{code}

或者您可以创建 SingletonTypeInjectableProvider 后代,使用 @Provider 对其进行注释将其添加为类。你可以在任何你需要的地方以及标准 Jersey 注入的地方注入提供的实例。

于 2012-05-24T04:58:40.407 回答
1

有一个 jersey-spring 项目支持 Spring 依赖注入。用 SpringServlet 替换您的球衣 ServletContainer,将 ContextLoaderListener 添加到您的 web.xml,然后您可以将 bean 注入您的组件中。这是一个相当不错的设置演练

http://www.mkyong.com/webservices/jax-rs/jersey-spring-integration-example/

编辑

这是一个不需要添加任何依赖项的想法。创建您自己的 ServletContextListener,将您的对象添加到 ServletContext。然后将 ServletContext 注入到您的资源中

public class MyContextListener implements ServletContextListener
{

    @Override
    public void contextDestroyed(ServletContextEvent event)
    {
    }

    @Override
    public void contextInitialized(ServletContextEvent event)
    {
        ServletContext context = event.getServletContext();
        context.setAttribute(Foo.class.getName(), new FooImpl());
    }

}

然后在您的资源中

@Path("blah")
public class MyResource 
{
   private Foo foo;

   public MyResource(@Context ServletContext context)
   {
      foo = (Foo) context.getAttribute(Foo.class.getName());
   } 
}
于 2012-05-24T00:08:17.020 回答
1

除非您愿意,否则您不必使用外部库。有据可查的是,让 CDI 与 Jersey 正常工作目前是一件痛苦的事情。但是,我可以从经验中说,我自己可以做到这一点。自从我跳过这些障碍已经有一段时间了,但我似乎记得我们必须制作我们的资源无状态 EJB 才能让它工作。我可能采取了其他步骤,但我现在不记得了。

当 Jersey 2.0 出现时,这应该会变得容易得多,因为他们将切换到使用 Core CDI 实现而不是他们自己的实现。有关更多信息,请参阅此错误:

http://java.net/jira/browse/JERSEY-517

于 2012-10-18T16:37:57.927 回答