Внедрение зависимостей с помощью 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

donnie_armstrong
источник

Ответы:

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()вызова метода.

Joscarsson
источник
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.

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

Gjijon
источник
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

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

Guice-Bridge-jit-инжектор

Чой
источник
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() {
алокдж
источник