Неудовлетворенная зависимость при использовании аннотации OmniFaces @Param. Нет подходящего bean-компонента типа java.lang.String

Я пытаюсь использовать CDI вместо устаревших аннотаций ManagedBean/ManagedProperty и столкнулся с этим исключением в очень простом веб-приложении:

Error creating bean with name 'navigationController': Unsatisfied dependency expressed through field 'message'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject(), @org.omnifaces.cdi.Param(validatorAttributes=[], validatorMessage=, validators=[], converter=, pathIndex=-1, converterAttributes=[], converterClass=interface javax.faces.convert.Converter, label=, overrideGlobalBeanValidationDisabled=false, required=false, disableBeanValidation=false, name=, validatorClasses=[], converterMessage=, requiredMessage=)}

Я пытаюсь следовать примеру @Param в демонстрации OmniFaces по адресу http://showcase.omnifaces.org/cdi/Param.

Я перешел на страницу с http://localhost:8080/jsfSpringBootApp/nav.xhtml?message=My+message+from+MessageSource. Насколько я понимаю, bean-компонент NavigationController должен быть создан при переходе к nav.xhtml и что поле сообщения будет заполнено значением, взятым из параметра запроса.

IntelliJ также жалуется на аннотацию @Param:

Не удается найти bean-компонент, квалифицированный с помощью @Param

Спасибо за любую помощь. Я застрял на том, что попробовать дальше.

Весь проект находится по адресу https://[email protected]/david_maffitt/jsfspringbootapp.git.

Содержимое nav.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:p="http://primefaces.org/ui">

<h:body >
<f:view>

    <p:panel id="myPanelId" header="My Panel" style="margin-bottom:20px">
        <h:outputText value="My text output." />
        <h:outputText value="My text output from params: #{navigationController.action}" />
    </p:panel>

</f:view>
</h:body>
</html>

Содержимое NavigationController.java: пакет org.nrg.cap.jsfWebApp;

import org.omnifaces.cdi.Param;

import javax.enterprise.context.RequestScoped;
import javax.faces.annotation.ManagedProperty;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;

//@Component
//@Scope("request")
@Named
@RequestScoped
public class NavigationController implements Serializable {

    @Inject @Param
    private String message;


    public String showPage() {
        return ("fubar".equals(message))? "fubar": "snafu";
    }

    public void setAction(String message) {
        this.message = message;
    }
    public String getAction() {
        return message;
    }
}

pom.xml это

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.nrg.cap</groupId>
    <artifactId>jsfSpringBootApp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>JSF Spring Boot WebApp</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>

        <joinfaces.version>4.0.0</joinfaces.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.joinfaces</groupId>
                <artifactId>joinfaces-dependencies</artifactId>
                <version>${joinfaces.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.joinfaces</groupId>
            <artifactId>primefaces-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.joinfaces</groupId>
            <artifactId>omnifaces3-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>2.0.SP1</version>
        </dependency>
    </dependencies>

</project>

Я обнаружил, что мне пришлось добавить зависимость для cdi-api, потому что я выполняю развертывание на tomcat 9.0.13, в котором нет встроенного cdi. pom получен из проекта joinfaces.


person Maffitt    schedule 15.12.2018    source источник
comment
@Param - это аннотация CDI, и хотя Spring распознает @Named и @Inject, это нет, это не контейнер CDI, но все же Spring. Переход с Spring на реальный cdi скорее всего решит проблему   -  person Kukeltje    schedule 15.12.2018
comment
Вы используете соединения, это означает, что в основе лежит весенняя загрузка, но также вы используете CDI в качестве DI. Я думаю, что это анти-шаблон, предпочитаю использовать Spring DI..   -  person Armen Arzumanyan    schedule 17.12.2018


Ответы (3)


Аннотация OmniFaces @Param представляет собой аннотацию на основе CDI (реализации). И хотя Spring может использовать @Named и @Inject в качестве замены для @Component и @Autowired, как указано в В чем разница между @Inject и @Autowired в Spring Framework? Какой из них использовать при каких условиях? (для чего вам действительно нужно добавить 'CDI'api) это все еще не делает Spring настоящим контейнером CDI DI.

Это означает, что @Param не будет использоваться/интерпретироваться Spring, как это было задумано OmniFaces/CDI, и Spring пытается найти настоящий bean-компонент с аннотацией @Param, которой, конечно же, не существует. Следовательно, вы получаете ошибку, которую получаете.

Какое лучшее решение выходит за рамки этого вопроса, поскольку оно «в основном основано на мнении», но в основном они сводятся к двум вариантам.

  • Удалите Spring (загрузку) для DI, так как JSF 2.4 будет требовать CDI и встроенный пользователем Tomcat (или java-ee) по-другому, как описано в https://www.c2b2.co.uk/middleware-blog/how-to-create-microservices-with-tomcat.php (использование микросервисов не требуется!)
  • Используйте Spring полностью и переключитесь на Spring MVC и откажитесь от JSF и Omnifaces.

Я не буду утверждать, что у первого здесь мои предпочтения... Хммм, только что...

person Kukeltje    schedule 17.12.2018

Вы должны внести изменения в набор bean.xml bean-discovery-mode="all"> https://github.com/armdev/reactive-javaserver-faces/blob/master/reactive-jsf/src/main/webapp/WEB-INF/beans.xml

person Armen Arzumanyan    schedule 16.12.2018
comment
Хотя OP заявляет, что CDI используется для инъекции, OP использует не CDI для инъекции, а Spring (см. ошибку). Так что это не имеет никакого отношения к открытию фасоли или нет. И @Param аннотация даже не о настоящем бобе... - person Kukeltje; 16.12.2018

Я смотрю глубже: проект основан на Joinfaces, значит, в основе проекта лежит загрузка Spring. Итак, после использования CDI для DI. Измените аннотации CDI на Spring DI — @Inject на @Autowired. Объединение двух разных принципов DI не является хорошей идеей.

person Armen Arzumanyan    schedule 16.12.2018
comment
Опять же, текущие версии Spring угрожают аннотациям cdi, идентичным его собственным. И их изменение все равно не заставит Spring понять @Param. Единственный способ сделать эту работу - не использовать Spring для DI, а настоящий cdi или использовать "похожую" аннотацию вместо чистой Spring (чего я не знаю, так как я не использую spring - person Kukeltje; 17.12.2018
comment
Не согласен, Spring DI отличается от CDI. Для слияния проект должен быть переконфигурирован в соответствии с моим предыдущим ответом, но это анти-шаблон, и он добавляет слой сбоя. Я видел много проектов, где CDI и Spring использовались вместе, это были проекты, которые нельзя обновить и поддержать. Мы должны понимать, что поведение CDI и Spring меняется с каждой новой версией. - person Armen Arzumanyan; 17.12.2018
comment
Также JSF сам по себе не нуждается в слиянии с Spring. - person Armen Arzumanyan; 17.12.2018
comment
Нет, опять неправильно... Я не говорю о "слиянии". OP использует Spring для DI и добавил CDI API, чтобы заставить Spring распознавать @Inject и @Named, как см. аннотации stackoverflow.com/questions/7142622 это то, что делает OP. Это работает, но вы не можете использовать другие настоящие аннотации CDI, такие как OmniFaces @Param Таким образом, ожидания от OP неверны. Переключение на аннотации Spring не приводит к тому, что @Param из Omnifaces работает. Так что OP тоже нуждается в весенней замене. - person Kukeltje; 17.12.2018