web-dev-qa-db-fra.com

Pourquoi @PostConstruct rappelle-t-il à chaque fois que bean est @ViewScoped? JSF

J'utilise datatable sur la page et j'utilise un attribut binding pour le lier à mon bean de support. Ceci est mon code: -

<?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://Java.Sun.com/jsf/html"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
            <h:form prependId="false">

                <h:dataTable var="item" value="#{testBean.stringCollection}" binding="#{testBean.dataTable}">
                    <h:column>
                        <h:outputText value="#{item}"/>
                    </h:column>
                    <h:column>
                        <h:commandButton value="Click" actionListener="#{testBean.action}"/>
                    </h:column>
                </h:dataTable>

            </h:form>

    </h:body>
</html>

C'est ma fève: -

package managedBeans;

import Java.io.Serializable;
import Java.util.ArrayList;
import Java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.html.HtmlDataTable;

@ManagedBean(name="testBean")
@ViewScoped
public class testBean implements Serializable {

    private List<String> stringCollection;

    public List<String> getStringCollection() {
        return stringCollection;
    }

    public void setStringCollection(List<String> stringCollection) {
        this.stringCollection = stringCollection;
    }

    private HtmlDataTable dataTable;

    public HtmlDataTable getDataTable() {
        return dataTable;
    }

    public void setDataTable(HtmlDataTable dataTable) {
        this.dataTable = dataTable;
    }

    @PostConstruct
    public void init(){
        System.out.println("Post Construct fired!!");
        stringCollection = new ArrayList<String>();
        stringCollection.add("a");
        stringCollection.add("b");
        stringCollection.add("c");

    }

    public void action(){
        System.out.println("Clicked!!");

    }
}

S'il vous plaît, dites-moi pourquoi le @PostConstruct se déclenche à chaque fois que je clique sur le bouton? Il ne devrait être déclenché qu’une fois, aussi longtemps que je suis sur la même page, car mon bean est @ViewScoped. De plus, si je supprime l'attribut binding, tout fonctionne correctement et le rappel @PostConstruct ne se déclenche qu'une seule fois. Alors pourquoi chaque fois que j'utilise l'attribut de liaison? J'ai besoin d'un attribut de liaison et je souhaite effectuer des tâches d'initialisation telles que l'extraction des données d'un service Web, etc. une seule fois. Que devrais-je faire? Où devrais-je écrire ma tâche d'initialisation?

31
TCM

Intéressant, lorsque vous utilisez la liaison de composant sur un bean de vue, l'étendue de la vue se brise. 

Je ne sais pas s'il s'agit d'un bogue dans JSF2, je devrais d'abord lire l'intégralité de la spécification JSF2. Pour l'instant, le mieux est de supprimer la liaison de composant pour le moment et de transmettre l'élément sélectionné via la nouvelle syntaxe d'argument de méthode EL 2.2:

<h:dataTable var="item" value="#{testBean.stringCollection}">
    <h:column>
        <h:outputText value="#{item}"/>
    </h:column>
    <h:column>
        <h:commandButton value="Click" action="#{testBean.action(item)}"/>
    </h:column>
</h:dataTable>

Voir également:


Update (déc 2012): il s'agit bien d'un bogue dans JSF2. C'est un problème d'œuf de poule. Les beans de la vue sont stockés dans l'état d'affichage JSF. Ainsi, les haricots avec vue ne sont disponibles qu’après la phase de restauration de la vue. Cependant, l'attribut binding s'exécute pendant la phase d'affichage de la restauration, alors que les beans avec vue ne sont pas encore disponibles. Cela provoque la création d'une toute nouvelle instance de bean périmée de vue, qui est ensuite remplacée par le bean réel de périmètre de vue stocké dans l'état de vue JSF restauré.

Ceci est rapporté comme JSF, numéro 1492 et JSF spec isssue 787 qui sera fixé pour JSF 2.2. Jusque-là, votre meilleur choix est d’utiliser exclusivement la variable binding sur demande, ou de rechercher d’autres méthodes pour répondre à vos besoins fonctionnels.


Mise à jour (mars 2015): le correctif JSF 2.2 a été rétroporté vers Mojarra 2.1.18. Donc, si vous utilisez toujours JSF 2.0/2.1, vous feriez mieux de mettre à niveau au moins cette version. Voir aussi a.o. Qu'est-ce que la liaison de composant dans JSF? Quand est-il préférable d'être utilisé? et JSTL dans JSF2 Facelets ... a du sens?

32
BalusC

Comme d'autres l'ont dit, je dirais que la meilleure chose à faire est de supprimer la liaison de composant (vous n'en avez pas besoin ici).

Mais j'ajouterais que vous pouvez obtenir le même résultat que vous essayez de le faire d'une manière plus orientée objet en utilisant des paramètres d'action, comme ceci:

<h:commandButton value="Click" action="#{testBean.action(item)}"/>

... et dans votre code Java:

  public void action(Item item){
    System.out.println("Clicked!!" + item);
}
4
ymajoros

Si vous avez un bean avec vue et si vous souhaitez conserver les valeurs entrées dans le formulaire ou si vous ne souhaitez pas que postconstruct soit déclenché, vous devez renvoyer null à partir de votre méthode d'action.

Si vous renvoyez un résultat (par exemple, invalide) et que vous le dirigez ensuite vers la même page à l'aide de faces-config.xml, le bean viewcoped est recréé et provoque ainsi le déclenchement de postconstruct. 

0
Pramod Kankure

La réponse du balusc m'a beaucoup aidé. Je tiens à dire que le bogue avec la version 2.1.7 de Mojarra est utilisé. J'utilise actuellement la version 2.1.29-01 publiée en janvier 2015 et ce bogue est corrigé. Mon problème était lié à une tabview à un haricot avec vue. Avec cette version, je n'ai pas ce bogue et la liaison et postconstruct fonctionne bien. J'utilise Jboss 5.2 et je dois utiliser mojarra 2.1.x donc j'espère que cette réponse aidera d'autres personnes dans la même situation.

http://mvnrepository.com/artifact/com.Sun.faces/jsf-api/2.1.29-01http://mvnrepository.com/artifact/com.Sun. faces/jsf-impl/2.1.29-01

0
Luis Vidal

Autre solution:

  • Liaison de HtmlDataTable dans un bean de portée de demande.
  • Injectez ce bean scope de demande dans le bean view scope.

JBoss Seam utilise cette solution pour lier des composants JSF à un composant de portée de conversation.