Инъекция зависимости с Джерси 2.0


начиная с нуля, без каких-либо предыдущих Джерси 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");
    }
}

пом.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 93

6 ответов:

необходимо указать 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() вызов метода.

сначала просто ответить на комментарий в принимает ответ.

" что делает bind? Что если у меня есть интерфейс и реализация?"

он просто читает bind( implementation ).to( contract ). Вы можете альтернативная цепь .in( scope ). Область действия по умолчанию PerLookup. Так что если вы хотите синглтон, вы можете

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

есть еще и RequestScoped скачать

кроме того, вместо bind(Class).to(Class), вы можете также bind(Instance).to(Class), который будет автоматически быть одиночка.


добавлять к принято отвечать

для тех, кто пытается выяснить, как зарегистрировать свой AbstractBinder реализация в веб.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;
    }
}

The @Provider аннотация должна позволять Feature быть выбранным вверх сканированием пакета. Или без сканирования пакета, вы можете явно прописать Feature на web.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>

Читайте Также:

и для общей информации из Джерси документация


обновление

фабрики

помимо основной привязки в принятом ответе, у вас также есть фабрики, где вы можете иметь более сложное создание логика, а также иметь доступ к контекстной информации запроса. Например

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 в своем классе ресурсом.

выбранный ответ датируется некоторое время назад. Это не практично, чтобы объявить все привязки в пользовательском HK2 Биндер. Я использую Tomcat, и мне просто нужно было добавить одну зависимость. Несмотря на то, что он был разработан для Glassfish, он идеально вписывается в другие контейнеры.

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

убедитесь, что ваш контейнер является правильно настроенный (см. документацию).

поздно, но я надеюсь, что это поможет кому-то.

у меня есть мой JAX RS определяется следующим образом:

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

затем, в моем коде, наконец, я могу ввести:

@Inject
SomeManagedBean bean;

В моем случае SomeManagedBean - это компонент ApplicationScoped.

надеюсь, что это поможет кому-нибудь.

Oracle рекомендует добавить аннотацию @Path ко всем типам, которые будут введены при объединении JAX-RS с CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Хотя это далеко не идеально (например, вы получите предупреждение от Джерси при запуске), я решил воспользоваться этим маршрутом, что избавляет меня от поддержания всех поддерживаемых типов в связующем.

пример:

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

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

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

Если вы предпочитаете использовать Guice и вы не хотите объявлять все привязки, вы также можете попробовать этот адаптер:

guice-мост-jit-инжектор