Nano Hash - криптовалюты, майнинг, программирование

Как интегрировать JAX-RS с CDI в контейнер Servlet 3.0

У меня есть веб-приложение, работающее в контейнере Servlet 3.0 (Jetty 9.0.4) с использованием JSF 2.2 (Mojorra 2.1.3) и CDI 1.1 (Weld 2.0.3). Полноценный сервер приложений не используется. В этом приложении у меня также есть класс ресурсов JAX-RS 2.0 (Jersey 2.2), обслуживающий запросы REST. Я интегрировал привязку JAXB, а также сортировку JSON (Jackson 2.2). Я использую Maven 3.0.5 для управления сборкой. Это соответствующие части моей настройки проекта:

Maven пом.xml:

...
<dependencies>

    <!-- Servlet 3.0 API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>

    <!-- Contexts and Dependency Injection for Java EE -->
    <dependency>
        <groupId>org.jboss.weld.servlet</groupId>
        <artifactId>weld-servlet</artifactId>
        <version>2.0.3.Final</version>
    </dependency>

    <!-- JavaServer Faces -->
    <dependency>
        <groupId>com.sun.faces</groupId>
        <artifactId>jsf-api</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>com.sun.faces</groupId>
        <artifactId>jsf-impl</artifactId>
        <version>2.2.2</version>
    </dependency>

    <!-- JAX-RS RESTful Web Services -->
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>2.2</version>
    </dependency>

    <!-- JSON Mapping Framework -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.2</version>
    </dependency>
</dependencies>
...

Дескриптор развертывания web.xml:

...
<context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
</context-param>

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/jsf/*</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>my.package.config.RestApplication</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>  

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

<listener>
    <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>

<listener>
    <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>

<resource-env-ref>
    <description>Object factory for the CDI Bean Manager</description>
    <resource-env-ref-name>BeanManager</resource-env-ref-name>
    <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>
...

Класс корневых ресурсов JAX-RS:

@Path("/person")
public class PersonController
{
    @Inject
    private PersonService personService;

    @GET
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public List<Person> getAllPersons()
    {
        return personService.getAll();
    }

    @GET
    @Path("/{index}")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Person getPerson(@PathParam("index") int index)
    {
        return personService.get(index);
    }

    @POST
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public void savePerson(Person person)
    {
        personService.add(person);
    }
}

Конфигурация приложения JAX-RS:

public class RestApplication extends ResourceConfig
{
    public RestApplication()
    {
        // For JSON binding
        register(new JacksonFeature());
        register(new ApplicationBinder());
        packages("my.package.controller");
    }
}

Конфигурация привязки инъекции JAX-RS:

public class ApplicationBinder extends AbstractBinder
{
    @Override
    protected void configure()
    {
        // Means something like: bind the field at an injection point of type PersonService to an instance of type PersonService
        bind(PersonService.class).to(PersonService.class);
    }
}

И, наконец, управляемый компонент JSF:

@Named
@SessionScoped
public class PersonBean implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Inject
    private PersonService personService;

    private Person newPerson = new Person();

    public List<Person> getAll()
    {
        return personService.getAll();
    }

    public Person getNewPerson()
    {
        return newPerson;
    }

    public void setNewPerson(Person newPerson)
    {
        this.newPerson = newPerson;
    }

    public Gender[] getGenders()
    {
        return Gender.values();
    }

    public String saveNewPerson()
    {
        personService.add(newPerson);
        newPerson = new Person();

        return "index";
    }
}

В конце концов, я хочу иметь возможность использовать одни и те же экземпляры службы на уровне приложения в классах ресурсов REST, а также в компонентах JSF, но я не могу заставить CDI и JAX-RS работать вместе.

Часть JSF/CDI работает нормально, но внедрение в классы ресурсов REST на самом деле не работает. Я читал несколько статей, в которых были показаны два разных подхода к объединению CDI и JAX-RS: первый — аннотировать класс ресурсов REST с помощью @ManagedBean, чтобы экземпляр класса создавался контейнером CDI и управлялся JAX. -RS контейнер:

@ManagedBean
@Path("/person")
public class PersonController
{
    @Inject
    private PersonService personService;
    ...

Второй подход заключается в том, чтобы дать классу область CDI, например. @RequestScoped, чтобы экземпляр класса создавался и управлялся контейнером CDI.

@Path("/person")
@RequestScoped
public class PersonController
{
    @Inject
    private PersonService personService;
    ...

Ни один из подходов не работает для меня. Я всегда получаю следующее исключение:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=PersonService,parent=PersonController,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,5643079)
    at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
    at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:208)
    at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:231)
    at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:328)
    at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:454)
    at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
    at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2296)
    at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:590)
    at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:577)
    at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:172)

Но эта ошибка исчезает при изменении конфигурации привязки инъекции на:

bind(PersonServiceImpl.class).to(PersonService.class);

Теперь инъекция как-то работает, но для каждого REST-запроса я получаю новый экземпляр PersonServiceImpl, даже если эта служба находится в области приложения. Для меня это показатель того, что компонент JAX-RS полностью отделен от материала CDI и живет в совершенно другой среде или контейнере, чем материал CDI/JSF.

Поэтому мне действительно интересно, как заставить эти две концепции работать вместе в чистом контейнере сервлета 3.0.


Ответы:


1

Я решил свою проблему.

Проблема в том, что реализация JAX-RS на Джерси использует инфраструктуру внедрения зависимостей HK2, и эта инфраструктура просто не знает о компонентах CDI. И, следуя идее принятого ответа в этот пост, я делаю CDI-бины доступными для привязок внедрения HK2, и теперь внедрение моих бинов в области приложения работает нормально.

Но мне действительно интересно, почему так сложно объединить две составные части Java EE.

Обновление: как упомянул в комментарии Г. Демеки, это решение больше не нужно! Но это помогло мне, когда я задавал этот вопрос.

02.10.2013
  • У вас есть общедоступный пример кода для этого? Я тоже с этим борюсь. 05.03.2014
  • Прости за мой запоздалый ответ. К счастью, у меня все еще есть очень простой пример проекта, который ничего не делает, кроме внедрения службы в класс контроллера JAX-RS. Затем эти службы вызываются с помощью модульных тестов, чтобы убедиться, что используется один и тот же внедренный экземпляр. Честно говоря, я давно не имел дела с такими вещами. Но я хотел бы поделиться им с вами и надеюсь, что он поможет вам разобраться в основных частях: file-upload.net/download-8733326/sample_project.zip.html 21.03.2014
  • Нет, это неправильный путь, так как использование пользовательской привязки HK больше не требуется начиная с JEE 7. Все, что вам нужно, чтобы заставить работать Jersey и CDI, — это добавить зависимость к jersey-gf-cdi-ban-custom-hk2-binding 31.07.2015
  • Да ладно, этому вопросу уже два года. Этот jersey-gf-cdi-ban-custom-hk2-binding в то время был недоступен. Но спасибо за эту подсказку. 31.07.2015
  • Похоже, вы правы, минус снят, извините. В то время (октябрь 2013 г.) был доступен только jersey-gf-cdi, но не уверен, что этого было бы достаточно. 02.09.2015
  • @ G.Demecki Как мне заставить это работать? Я добавил зависимость maven к jersey-gf-cdi-ban-custom-hk2-binding, но все равно получаю ошибку org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl 29.05.2017

  • 2

    Вы можете просто включить эту зависимость, чтобы включить интеграцию Jersey-CDI.

    <dependency>
        <groupId>org.glassfish.jersey.containers.glassfish</groupId>
        <artifactId>jersey-gf-cdi</artifactId>
        <version>2.2</version>
    </dependency>
    

    У меня работает с Jersey 2.8, Jetty 9.1 и Weld 2.2.0.SP1.

    13.05.2014

    3

    Я создал пример проекта с Embedded Jetty, Jersey 2 и CDI https://drive.google.com/file/d/0B9j0dIS5bS0wSEg0NTk2eGpPSGs/view?usp=sharing

    Пожалуйста, дайте мне знать, если вы обнаружите какие-либо проблемы

    03.01.2015
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..