Внедрение зависимостей с помощью Jersey 2.0


108

Начав с нуля без каких-либо предварительных знаний о Джерси 1.x, мне трудно понять, как настроить внедрение зависимостей в моем проекте Джерси 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

Ответы:


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");
    }
}

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


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

Да, EntityManager специфичен для JPA (Java EE). Спасибо за комментарий, я открою еще один вопрос, если столкнусь с конкретной проблемой!
— Johannes Staehlin

Для справки, JPA также работает на Java SE. oracle.com/technetwork/java/javaee/tech/…
— prefabSOFT 08

2
Что делает привязка? Что, если у меня есть интерфейс и реализация?
— Dejell

53

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

«Что делает 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реализацию в вашем web.xml (т.е. вы не используете a 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в свой класс ресурсов.


Я мог бы зарегистрировать свой класс связующего только с помощью реализации ResourceConfig, как показано в принятом ответе. Класс объектов не нужен.
— Патрик Куреваар

Использование 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это bean-компонент ApplicationScoped.

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


3

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() {..}
}

1
Ссылка мертва, должна указывать сюда
— Хэнк


0

Для меня это работает без использования, 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 соответственно).


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() {
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.