使用Jersey 2.0进行依赖注入

115

如果没有之前 Jersey 1.x 的知识基础,我很难理解如何在我的 Jersey 2.0 项目中设置依赖注入。

我知道 HK2 在 Jersey 2.0 中可用,但似乎找不到有关 Jersey 2.0 集成的文档。

@ManagedBean
@Path("myresource")
public class MyResource {

    @Inject
    MyService myService;

    /**
     * Method handling HTTP GET requests. The returned object will be sent
     * to the client as "text/plain" media type.
     *
     * @return String that will be returned as a text/plain response.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/getit")
    public String getIt() {
        return "Got it {" + myService + "}";
    }
}

@Resource
@ManagedBean
public class MyService {
    void serviceCall() {
        System.out.print("Service calls");
    }
}

pom.xml

<properties>
    <jersey.version>2.0-rc1</jersey.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-common</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey</groupId>
        <artifactId>jax-rs-ri</artifactId>
    </dependency>
</dependencies>

我可以启动容器并提供我的资源,但只要我在MyService中添加@ Inject,框架就会抛出异常:

SEVERE: Servlet.service() for servlet [com.noip.MyApplication] in context with path [/jaxrs] threw exception [A MultiException has 3 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.noip.MyResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.noip.MyResource
] with root cause
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
    at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)


我的入门项目已经上传至GitHub:https://github.com/donaldjarmstrong/jaxrs

8个回答

111
你需要定义一个AbstractBinder并在JAX-RS应用程序中注册它。该绑定器指定依赖注入如何创建你的类。
public class MyApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(MyService.class).to(MyService.class);
    }
}

当在类型为MyService.class的参数或字段上检测到@Inject时,将使用类MyService实例化它。要使用此绑定器,需要将其注册到JAX-RS应用程序中。在您的web.xml中,定义一个JAX-RS应用程序,如下所示:

<servlet>
  <servlet-name>MyApplication</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>com.mypackage.MyApplication</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>MyApplication</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

实现MyApplication类(在init-param中指定)。

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(new MyApplicationBinder());
        packages(true, "com.mypackage.rest");
    }
}

在类的构造函数中注册指定依赖注入的绑定器,同时我们还使用packages()方法调用告知应用程序在何处找到REST资源(在你的情况下为MyResource)。


1
EntityManager怎么样?有什么提示可以绑定它,这样我就可以通过@PersistenceContext注入它了吗? - Johannes Staehlin
4
我不确定 EntityManager 是什么,但根据 http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html 看来它是一个接口。您可以使用 bind(EntityManagerImpl.class).to(EntityManager.class) 进行绑定(这将绑定实现接口 EntityManager 的类 EntityManagerImpl)。如果您需要使用工厂,请查看 AbstractBinder 中的 bindFactory()。如果您需要帮助,请创建一个新问题(在评论中我没有足够的空间回答)。此外,我不确定您是否应该使用 @PersistentContext,只需为所有内容使用 @Inject 即可。 - joscarsson
是的,EntityManager 是 JPA(Java EE)特定的。感谢您的评论,如果我遇到具体问题,我会开另一个问题! - Johannes Staehlin
仅供参考,JPA也可以在Java SE上运行。 http://www.oracle.com/technetwork/java/javaee/tech/persistence-jsp-140049.html - prefabSOFT
2
bind函数是做什么用的?如果我有一个接口和一个实现,会怎样呢? - Dejell
显示剩余3条评论

58

首先回答一下对接受答案的评论。

"bind是做什么的?如果我有一个接口和一个实现怎么办?"

它只是简单地读取bind(implementation).to(contract)。您可以交替链接.in(scope)。默认范围为PerLookup。因此,如果您想要一个单例模式,您可以

bind( implementation ).to( contract ).in( Singleton.class );

还有一个可用的RequestScoped

而且,你可以使用bind(Instance).to(Class)代替bind(Class).to(Class),这将自动成为单例模式。


补充被接受的答案

对于那些试图弄清楚如何在web.xml中注册您的AbstractBinder实现的人(即您没有使用ResourceConfig),似乎绑定器不会通过程序包扫描进行发现,即:

<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
    <param-name>jersey.config.server.provider.packages</param-name>
    <param-value>
        your.packages.to.scan
    </param-value>
</init-param>

或者也可以选择这个

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>
        com.foo.YourBinderImpl
    </param-value>
</init-param>
为了让它工作,我不得不实现一个Feature(文档链接)
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;

@Provider
public class Hk2Feature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(new AppBinder());
        return true;
    }
}

@Provider 注释应该允许 Feature 被包扫描捕获。或者如果没有包扫描,您可以在 web.xml 中显式注册 Feature

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>
            com.foo.Hk2Feature
        </param-value>
    </init-param>
    ...
    <load-on-startup>1</load-on-startup>
</servlet>

参见:

以及来自Jersey文档的一般信息


更新

工厂

除了接受答案中的基本绑定外,您还可以使用工厂,其中可以具有更复杂的创建逻辑,并且还可以访问请求上下文信息。例如:

public class MyServiceFactory implements Factory<MyService> {
    @Context
    private HttpHeaders headers;

    @Override
    public MyService provide() {
        return new MyService(headers.getHeaderString("X-Header"));
    }

    @Override
    public void dispose(MyService service) { /* noop */ }
}

register(new AbstractBinder() {
    @Override
    public void configure() {
        bindFactory(MyServiceFactory.class).to(MyService.class)
                .in(RequestScoped.class);
    }
});

那么,您就可以将 MyService 注入到您的资源类中。


我可以通过ResourceConfig实现来注册我的绑定器类,就像接受的答案中所示。不需要Feature类。 - Patrick Koorevaar
即使调用了Hk2Feature上的configure()方法,仍然需要使用web.xml文件。否则请求资源时会抛出NullPointerException异常。@PaulSamsotha - bytesandcaffeine
It simply reads”这句话并没有让我真正理解它的作用。此外,在bind(Instance).to(Class)的情况下,你从哪里获取实例?因为我们正在谈论AbstractBinder中的configure,对吧? - BairDev

13

所选答案的日期相对较早。在自定义HK2绑定程序中声明每个绑定是不切实际的。 我正在使用Tomcat,我只需要添加一个依赖项。尽管它是为Glassfish设计的,但它完美地适用于其他容器。

   <dependency>
        <groupId>org.glassfish.jersey.containers.glassfish</groupId>
        <artifactId>jersey-gf-cdi</artifactId>
        <version>${jersey.version}</version>
    </dependency>

确保您的容器也已正确配置(请参阅文档)。


最后一行(确保您的容器也正确配置)有点含糊不清。能否提供帮助?我们在哪里使用注释? - markthegrea
我们使用Weld进行依赖注入,这需要一些特殊的配置才能与Tomcat(我们的应用程序“容器”)配合使用。如果您使用Spring,则可以直接使用。 - Olivier Tonglet

5
晚了一点,但我希望这能帮到某些人。
我的JAX RS定义如下:
@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {

然后,在我的代码中,最终我可以注入以下内容:
@Inject
SomeManagedBean bean;

在我的例子中,SomeManagedBean 是一个应用作用域的 bean。
希望这能对任何人有所帮助。

3
Oracle建议在将JAX-RS与CDI组合时,为所有要注入的类型添加@Path注释: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm 虽然这并不完美(例如,在启动时您将收到来自Jersey的警告),但我决定采取这种方法,这样可以避免我维护所有支持的类型的绑定程序。

示例:

@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
  .. 
}

@Path("my-path")
class MyProvider {
  @Inject ConfigurationService _configuration;

  @GET
  public Object get() {..}
}

1
连接已失效,应该指向此处 - Hank

0
如果您喜欢使用Guice并且不想声明所有绑定,您也可以尝试使用此适配器: guice-bridge-jit-injector

0

对我来说,如果我在我的 Web 应用程序中包含以下依赖项(运行在 Tomcat 8.5、Jersey 2.27 上),则不需要使用 AbstractBinder

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>${jersey-version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext.cdi</groupId>
    <artifactId>jersey-cdi1x</artifactId>
    <version>${jersey-version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>${jersey-version}</version>
</dependency>

对我来说,它可以与CDI 1.2 / CDI 2.0一起使用(分别使用Weld 2 / 3)。


0

Jersey restful服务和Tomcat服务器需要依赖,其中${jersey.version}为2.29.1。

    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>2.0.SP1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>${jersey.version}</version>
    </dependency>

基本代码如下:
@RequestScoped
@Path("test")
public class RESTEndpoint {

   @GET
   public String getMessage() {

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接