web-dev-qa-db-fra.com

Comment l'attribut 'binding' fonctionne-t-il dans JSF? Quand et comment devrait-il être utilisé?

Il y a beaucoup de matériaux qui différencient value attribut et binding attribut dans JSF.

Je suis intéressé par la façon dont les deux approches diffèrent l'une de l'autre. Donné:

public class User {
    private String name;
    private UICommand link;

    // Getters and setters omitted.
}
<h:form>
    <h:commandLink binding="#{user.link}" value="#{user.name}" />
</h:form>

C'est assez simple ce qui se passe lorsqu'un attribut value est spécifié. Le getter s'exécute pour renvoyer la valeur de la propriété name du bean User. La valeur est imprimée en sortie HTML.

Mais je ne comprenais pas comment binding fonctionnait. Comment le code HTML généré maintient-il une liaison avec la propriété link du bean User?

Ci-dessous se trouve la partie pertinente de la sortie générée après embellissement manuel et commentaire (notez que l'identifiant j_id_jsp_1847466274_1 a été généré automatiquement et qu’il existe deux widgets d’entrée cachés). J'utilise JSF RI de Sun, version 1.2.

<form action="/TestJSF/main.jsf" enctype="application/x-www-form-urlencoded"
    id="j_id_jsp_1847466274_1" method="post"  name="j_id_jsp_1847466274_1">
    <input name="j_id_jsp_1847466274_1" type="hidden" value="j_id_jsp_1847466274_1">
    <a href="#" onclick="...">Name</a>
    <input autocomplete="off" id="javax.faces.ViewState" name="javax.faces.ViewState"
        type="hidden" value="-908991273579182886:-7278326187282654551">
</form>

Où le binding est-il stocké ici?

71
John

Comment ça marche?

Quand une vue JSF (fichier Facelets/JSP) est construite/restaurée, une arborescence de composants JSF est générée. À ce moment, les heure de création de la vue , tous les attributs binding sont évalués ( avec id attributs et gestionnaires de balises tels que JSTL ). Lorsque le composant JSF doit être créé avant d'être ajouté à l'arborescence des composants, JSF vérifie si l'attribut binding renvoie un composant précréé (c'est-à-dire non -null) et, le cas échéant, utilisez-le. . Si ce n'est pas déjà créé, JSF créera automatiquement le composant "de la manière habituelle" et invoquera le créateur derrière l'attribut binding avec l'argument l'instance de composant créée automatiquement.

Dans les effets, il lie une référence de l'occurrence de composant dans l'arbre de composants à une variable étendue. Ces informations ne sont en aucun cas visibles dans la représentation HTML générée du composant lui-même. Ces informations ne sont en aucun cas pertinentes pour la sortie HTML générée. Lorsque le formulaire est soumis et que la vue est restaurée, l'arborescence des composants JSF est simplement reconstruite à partir de zéro et tous les attributs binding seront simplement réévalués comme décrit dans le paragraphe ci-dessus. Une fois l'arborescence des composants recréée, JSF restaure l'état d'affichage JSF dans l'arborescence des composants.

Les instances de composant sont limitées à la demande!

Il est important de savoir et de comprendre que les instances de composant concret sont effectivement limitées à la demande. Elles sont nouvellement créées à chaque demande et leurs propriétés sont remplies avec les valeurs de l'état d'affichage JSF pendant la phase d'affichage de restauration. Donc, si vous liez le composant à une propriété d'un bean de support, le bean de support ne devrait absolument pas être dans une portée plus large que celle de la requête. Voir aussi spécitication JSF 2. chapitre 3.1.5:

3.1.5 Liaisons de composants

...

Les liaisons de composants sont souvent utilisées avec des JavaBeans instanciés dynamiquement via la fonction de création de beans gérés (voir Section 5.8.1 "VariableResolver et le VariableResolver par défaut"). Il est vivement recommandé aux développeurs d'applications de placer les beans gérés pointés par des expressions de liaison de composant dans l'étendue "request". la portée de l'application nécessiterait la sécurité des threads, car les instances UIComponent dépendent de l'exécution à l'intérieur d'un seul thread. Il y a également des impacts potentiellement négatifs sur la gestion de la mémoire lors du placement d'une liaison de composant dans l'étendue "session".

Sinon, les occurrences de composant sont partagées entre plusieurs requêtes, ce qui peut entraîner des erreurs " ID de composant en double " et des comportements "étranges", car les validateurs, les convertisseurs et les écouteurs déclarés dans la vue sont rattachés à l'instance de composant existante. de demandes précédentes. Les symptômes sont clairs: ils sont exécutés plusieurs fois, une fois de plus avec chaque demande dans la même étendue que celle à laquelle le composant est lié.

Et, sous une charge importante (c'est-à-dire lorsque plusieurs requêtes HTTP différentes (threads) accèdent et manipulent la même instance de composant en même temps), vous pouvez être confronté tôt ou tard à un blocage d'application, par exemple. Stuck thread à UIComponent.popComponentFromEL , ou Java Threads à 100% d'utilisation du processeur à l'aide de richfaces UIDataAdaptorBase et de son HashMap interne , ou même de "étranges" IndexOutOfBoundsException ou ConcurrentModificationException venant directement du code source de l'implémentation JSF alors que JSF est occupé à sauvegarder ou à restaurer l'état d'affichage (c'est-à-dire que la trace de la pile indique les méthodes saveState() ou restoreState(), etc.).

Utiliser binding sur une propriété de bean est une mauvaise pratique

Quoi qu'il en soit, en utilisant binding de cette manière, la liaison d'une instance de composant entière à une propriété de bean, même sur un bean étendu de requête, constitue dans JSF 2.x un cas d'utilisation plutôt rare et ne constitue généralement pas la meilleure pratique. Cela indique une odeur de design. Vous déclarez normalement des composants dans la vue et associez leurs attributs d'exécution tels que value, et peut-être d'autres tels que styleClass, disabled, rendered, etc., à la normale propriétés du haricot. Ensuite, vous manipulez exactement la propriété de bean souhaitée au lieu de saisir tout le composant et d’appeler la méthode setter associée à l’attribut.

Dans les cas où un composant doit être "construit dynamiquement" sur la base d'un modèle statique, mieux vaut utiliser voir les étiquettes de temps de construction telles que JSTL , si nécessaire dans un fichier de balise , au lieu de createComponent(), new SomeComponent(), getChildren().add() et tout le reste. Voir aussi Comment refactoriser un extrait de code JSP ancien en un équivalent JSF?

Ou, si un composant doit être "rendu dynamiquement" sur la base d'un modèle dynamique, utilisez simplement un composant itérateur (<ui:repeat>, <h:dataTable>, etc). Voir aussi Comment ajouter dynamiquement des composants JSF .

Les composants composites sont une histoire complètement différente. C'est tout à fait légitime de lier des composants à l'intérieur d'un <cc:implementation> au composant de support (c’est-à-dire au composant identifié par <cc:interface componentType>. Voir aussi a.o. Split Java.util.Date sur deux champs h: inputText représentant l'heure et la minute avec f: convertDateTime et Comment implémenter une liste dynamique avec un composant composite JSF 2.0?

N'utilisez que binding dans la portée locale

Cependant, vous souhaiterez parfois connaître l'état d'un composant différent depuis un composant particulier, plus souvent dans les cas d'utilisation liés à la validation dépendante de l'action/la valeur. Pour cela, l'attribut binding peut être utilisé, mais pas en combinaison avec une propriété de bean. Vous pouvez simplement spécifier un nom de variable unique dans l'étendue EL locale dans l'attribut binding comme so binding="#{foo}" et le composant est pendant la réponse au rendu ailleurs dans la même vue directement en tant que UIComponent référence disponible par #{foo}. Voici plusieurs questions connexes pour lesquelles une telle solution a été utilisée dans la réponse:

Voir également:

121
BalusC

chaque composant JSF se transforme en HTML et contrôle totalement le code HTML qu'il produit. JSF peut utiliser de nombreuses astuces, et l’une de ces astuces à utiliser dépend de l’implémentation de JSF que vous utilisez.

  • Assurez-vous que chaque entrée de depuis a un nom totalement unique. Ainsi, lorsque le formulaire est soumis à l'arborescence du composant qui l'a restituée, il est facile de savoir où chaque composant peut lire son formulaire de valeur.
  • Le composant JSF peut générer du javascript qui est renvoyé au sereur. Le javascript généré sait également où chaque composant est lié, car il a été généré par le composant.
  • Pour des choses comme hlink, vous pouvez inclure des informations de liaison dans l'URL en tant que paramètres de requête ou en tant que partie de l'URL elle-même ou en tant que paramètres matrx. pour des exemples.

    http:..../somelink?componentId=123 permettrait à jsf de regarder dans l’arborescence des composants pour voir que le lien 123 a été cliqué. ou il pourrait e htp:..../jsf;LinkId=123

Le moyen le plus simple de répondre à cette question consiste à créer une page JSF avec un seul lien, puis à examiner le résultat HTML généré. De cette façon, vous saurez exactement comment cela se passe avec la version de JSF que vous utilisez.

1
ams