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

Начиная с нуля без каких-либо предыдущих знаний о 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 ответов

Решение

Вы должны определить 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");
    }
}

Связующее, определяющее внедрение зависимостей, регистрируется в конструкторе класса, и мы также сообщаем приложению, где найти ресурсы REST (в вашем случае MyResource) с использованием packages() вызов метода.

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

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

Это просто читает 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 быть найденным при сканировании пакета. Или без сканирования пакета, вы можете явно зарегистрировать 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() {..}
}

Для меня это работает без AbstractBinder если я включу следующие зависимости в свое веб-приложение (работает на 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 соответственно).

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

Guice-мост-JIT-инжектор

Зависимость, необходимая для спокойного обслуживания трикотажа, а 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() {
Другие вопросы по тегам