Ін'єкційна залежність за допомогою Jersey 2.0


108

Починаючи з нуля, не маючи попередніх знань 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");
    }
}

пом.хмл

<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?
Йоганнес Стейлін

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). Дякую за ваш коментар, я відкрию ще одне питання, якщо я зіткнувся з конкретною проблемою!
Йоганнес Шелін

Щойно для запису, JPA також працює на Java SE. oracle.com/technetwork/java/javaee/tech/…
prefabSOFT

2
Що робить зв’язок? Що робити, якщо у мене є інтерфейс та реалізація?
Dejell

52

Спочатку просто відповісти на коментар у відповіді, що приймає.

"Що робити зв'язуванням? Що робити, якщо у мене є інтерфейс та реалізація?"

Він просто читає 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>

Щоб змусити його працювати, мені довелося реалізувати a 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, як показано у прийнятій відповіді. Клас Feature не потрібен.
Патрік Коореваар

Використовуючи, web.xmlнавіть якщо configure()увімкнено " Hk2Featureon", запит на ресурс кидає a 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, він працює поза коробкою.
отонглет

5

Пізно, але я сподіваюся, що це комусь допоможе.

У мене JAX RS визначений так:

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

Тоді в свій код нарешті я можу ввести:

@Inject
SomeManagedBean bean;

У моєму випадку SomeManagedBean це боб 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

Якщо ви віддаєте перевагу використовувати Guice і не хочете оголошувати всі прив’язки, ви можете також спробувати цей адаптер:

guice-bridge-jit-injector


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.