Jersey 2.0的依赖注入


108

从没有任何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>

我可以使容器启动并提供资源,但是一旦将@Inject添加到MyService,框架就会引发异常:

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

Answers:


107

您需要定义一个AbstractBinder并将其注册到您的JAX-RS应用程序中。活页夹指定依赖项注入应如何创建类。

public class MyApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(MyService.class).to(MyService.class);
    }
}

@Inject在类型的参数或字段上检测到时,将MyService.class使用类将其实例化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");
    }
}

指定依赖项注入的活页夹已在类的构造函数中注册,并且我们还MyResource通过packages()方法调用告诉应用程序在哪里找到REST资源(在您的情况下为)。


1
那EntityManager呢?任何提示如何绑定它,所以我可以通过@PersistenceContext注入它?
Johannes Staehlin

4
我不确定是什么EntityManager,但是根据docs.oracle.com/javaee/6/api/javax/persistence/判断,它似乎是一个接口。您可以使用绑定它bind(EntityManagerImpl.class).to(EntityManager.class)(将结合一个类EntityManagerImpl实现了接口EntityManager,如果你需要使用一个工厂,看看bindFactory()AbstractBinder。如果你需要帮助,这一点,请创建一个新的问题(我不会有空间另外,我不确定您应该使用@PersistentContext,还是对所有内容使用@Inject
joscarsson 2013年

是的,EntityManager是JPA(Java EE)专用的。感谢您的评论,如果遇到特定问题,我将打开另一个问题!
Johannes Staehlin 2013年

仅作记录,JPA也可以在Java SE上运行。oracle.com/technetwork/java/javaee/tech/…–
prefabSOFT

2
绑定有什么作用?如果我有接口和实现该怎么办?
Dejell 2014年

52

首先只是在接受答案中回答评论。

“绑定有什么作用?如果我有接口和实现怎么办?”

它只是读bind( implementation ).to( contract )。您可以选择连锁.in( scope )。的默认范围PerLookup。因此,如果您想要单身人士,可以

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

还有一个RequestScoped可用的

另外,除了,bind(Class).to(Class)您还可以选择bind(Instance).to(Class),它将自动成为单例。


添加到接受的答案

对于那些试图弄清楚如何AbstractBinder在web.xml中注册您的实现的人(即您没有使用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由包扫描被拾起。或者没有包扫描,你可以明确地登记Featureweb.xml

<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实现注册我的资料夹类,如接受的答案所示。不需要要素类。
Patrick Koorevaar,2016年

web.xml即使调用了configure()on Hk2Feature,也要使用,但请求资源会引发NullPointerException。@PaulSamsotha
bytesandcaffeine

12

所选答案可追溯到不久前。在自定义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,则可以直接使用。
otonglet

5

迟了,但我希望这对某人有帮助。

我的JAX RS定义如下:

@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {

然后,最终可以在我的代码中注入:

@Inject
SomeManagedBean bean;

就我而言,SomeManagedBean是一个ApplicationScoped 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

对我而言,AbstractBinder如果在我的Web应用程序中包含以下依赖项(在Tomcat 8.5,Jersey 2.27上运行),则它不起作用:

<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

球衣静态服务所需的依赖关系,而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() {
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.