首页 文章

使用Jersey 2.0进行依赖注入

提问于
浏览
95

从头开始没有任何以前的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

6 回答

  • 5

    您需要定义 AbstractBinder 并在JAX-RS应用程序中注册它 . Binders 指定依赖注入应如何创建类 .

    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 ) .

  • 11

    首先只是回答接受答案中的评论 .

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

    它只是读取 bind( implementation ).to( contract ) . 你可以替代链 .in( scope ) . 默认范围为 PerLookup . 所以,如果你想要一个单身人士,你可以

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

    还有 RequestScoped 可用

    此外,您也可以 bind(Instance).to(Class) 代替 bind(Class).to(Class) ,它将自动成为单身人士 .


    添加到accepted answer

    对于那些试图弄清楚如何在你的web.xml中注册 AbstractBinder 实现的人(即你没有使用 ResourceConfig ),似乎不会通过包扫描发现 Binders ,即

    <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>
    

    See Also:

    以及来自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 注入资源类 .

  • 2

    选定的答案可以追溯到前一段时间 . 在自定义HK2 Binders 中声明每个绑定是不切实际的 . 我正在使用Tomcat,我只需添加一个依赖项 . 尽管它是专为Glassfish设计的,但它完全适合其他容器 .

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

    确保您的容器也已正确配置(see the documentation) .

  • 0

    迟到但我希望这有助于某人 .

    我的JAX RS定义如下:

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

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

    @Inject
    SomeManagedBean bean;
    

    在我的例子中, SomeManagedBean 是一个ApplicationScoped bean .

    希望这对任何人都有帮助 .

  • 95

    Oracle建议在将JAX-RS与CDI结合使用时将@Path注释添加到要注入的所有类型:http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm虽然这远非完美(例如你在启动时会收到来自Jersey的警告),但我决定采用这条路线,这样可以节省我在一个 Binders 中维护所有支持的类型 .

    例:

    @Singleton
    @Path("singleton-configuration-service")
    public class ConfigurationService {
      .. 
    }
    
    @Path("my-path")
    class MyProvider {
      @Inject ConfigurationService _configuration;
    
      @GET
      public Object get() {..}
    }
    
  • 45

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

    guice-bridge-jit-injector

相关问题