Wie funktioniert das Attribut 'Bindung' in JSF? Wann und wie soll es angewendet werden?

78

Es gibt viele Materialien, die valueAttribute und bindingAttribute in JSF unterscheiden.

Mich interessiert, wie sich beide Ansätze voneinander unterscheiden. Gegeben:

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>

Es ist ziemlich einfach, was passiert, wenn ein valueAttribut angegeben wird. Der Getter wird ausgeführt, um den nameEigenschaftswert der UserBean zurückzugeben. Der Wert wird in der HTML-Ausgabe gedruckt.

Aber ich konnte nicht verstehen, wie es bindingfunktioniert. Wie behält der generierte HTML-Code eine Bindung mit der linkEigenschaft der UserBean bei?

Unten finden Sie den relevanten Teil der generierten Ausgabe nach manueller Verschönerung und Kommentierung (beachten Sie, dass die ID j_id_jsp_1847466274_1automatisch generiert wurde und zwei versteckte Eingabe-Widgets vorhanden sind). Ich verwende Suns JSF RI, 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>

Wo ist das bindinghier gespeichert?

John
quelle

Antworten:

132

Wie funktioniert es?

Wenn eine JSF-Ansicht (Facelets / JSP-Datei) erstellt / wiederhergestellt wird, wird ein JSF-Komponentenbaum erstellt. In diesem Moment, der Erstellungszeit der Ansicht , werden alle bindingAttribute ausgewertet ( zusammen mit idAttributen und Taghandlern wie JSTL ). Wenn die JSF-Komponente erstellt werden muss, bevor sie zum Komponentenbaum hinzugefügt wird, prüft JSF, ob das bindingAttribut eine vorgefertigte Komponente (dh keine) zurückgibt null, und verwendet sie dann , wenn dies der Fall ist. Wenn es nicht vorgefertigt ist, erstellt JSF die Komponente automatisch "auf die übliche Weise" und ruft den dahinter liegenden Setter aufbinding Attribut mit der automatisch erstellten Komponenteninstanz als Argument auf.

In Effekten bindet es eine Referenz der Komponenteninstanz im Komponentenbaum an eine Variable mit Gültigkeitsbereich. Diese Informationen sind in der generierten HTML-Darstellung der Komponente selbst in keiner Weise sichtbar. Diese Informationen sind für die generierte HTML-Ausgabe ohnehin keineswegs relevant. Wenn das Formular gesendet und die Ansicht wiederhergestellt wird, wird der JSF-Komponentenbaum einfach von Grund auf neu erstelltbinding Attribute werden wie im obigen Absatz beschrieben neu bewertet. Nachdem der Komponentenbaum neu erstellt wurde, stellt JSF den JSF-Ansichtsstatus im Komponentenbaum wieder her.

Komponenteninstanzen haben einen Anforderungsbereich!

Es ist wichtig zu wissen und zu verstehen, dass die konkreten Komponenteninstanzen einen effektiven Anforderungsbereich haben. Sie werden bei jeder Anforderung neu erstellt und ihre Eigenschaften werden während der Wiederherstellungsansichtsphase mit Werten aus dem JSF-Ansichtsstatus gefüllt. Wenn Sie die Komponente an eine Eigenschaft einer Backing-Bean binden, sollte sich die Backing-Bean auf keinen Fall in einem breiteren Bereich als dem Anforderungsbereich befinden. Siehe auch JSF 2.0-Spezifikation Kapitel 3.1.5:

3.1.5 Komponentenbindungen

...

Komponentenbindungen werden häufig in Verbindung mit JavaBeans verwendet, die dynamisch über die Funktion zum Erstellen verwalteter Beans instanziiert werden (siehe Abschnitt 5.8.1 „VariableResolver und der StandardvariableResolver“). Es wird dringend empfohlen, dass Anwendungsentwickler verwaltete Beans, auf die durch Komponentenbindungsausdrücke verwiesen wird, im Bereich "Anfrage" platzieren. Dies liegt daran, dass das Platzieren im Sitzungs- oder Anwendungsbereich Thread-Sicherheit erfordern würde, da UIComponent-Instanzen davon abhängen, ob sie in einem einzelnen Thread ausgeführt werden. Es gibt auch potenziell negative Auswirkungen auf die Speicherverwaltung, wenn eine Komponentenbindung in den Sitzungsbereich gestellt wird.

Andernfalls werden Komponenteninstanzen von mehreren Anforderungen gemeinsam genutzt, was möglicherweise zu einer " doppelten Komponenten-ID" führt Fehlern " und "seltsamen" Verhaltensweisen führt, da in der Ansicht deklarierte Validatoren, Konverter und Listener aus früheren Anforderungen erneut an die vorhandene Komponenteninstanz angehängt werden. Die Symptome sind klar: Sie werden mehrmals ausgeführt, einmal mehr bei jeder Anforderung im selben Bereich, an den die Komponente gebunden ist.

Und unter hoher Last (dh wenn mehrere verschiedene HTTP-Anforderungen (Threads) gleichzeitig auf dieselbe Komponenteninstanz zugreifen und diese bearbeiten ) kann es früher oder später zu einem Anwendungsabsturz kommen, z. B. mit festgefahrenem Thread bei UIComponent.popComponentFromEL oder Java-Threads bei 100% CPU-Auslastung unter Verwendung von Richfaces UIDataAdaptorBase und seiner internen HashMap oder sogar etwas "seltsam" IndexOutOfBoundsExceptionoder ConcurrentModificationExceptiondirekt aus dem Quellcode der JSF-Implementierung stammend, während JSF damit beschäftigt ist, den Ansichtsstatus zu speichern oder wiederherzustellen (dh der Stack-Trace zeigt saveState()oder restoreState()Methoden und ähnliches an).

Die Verwendung bindingauf einer Bean-Eigenschaft ist eine schlechte Praxis

Unabhängig bindingdavon ist das Binden einer gesamten Komponenteninstanz an eine Bean-Eigenschaft auf diese Weise, selbst bei einer Bean mit Anforderungsbereich, in JSF 2.x ein eher seltener Anwendungsfall und im Allgemeinen nicht die beste Vorgehensweise. Es zeigt einen Designgeruch an. Sie erklären normalerweise Komponenten in der Ansicht Seite und bindet ihre Laufzeit Attribute wie value, und vielleicht auch andere mögen styleClass, disabled, rendered, usw, in den normalen Bean - Eigenschaften. Anschließend bearbeiten Sie einfach genau die gewünschte Bean-Eigenschaft, anstatt die gesamte Komponente zu erfassen und die dem Attribut zugeordnete Setter-Methode aufzurufen.

In den Fällen , wenn eine Komponente needs „dynamisch aufgebaut“ werden basierend auf einem statischen Modell ist besser zu nutzen Ansicht Kompilierzeit Tags wie JSTL , falls erforderlich in einer Tag - Datei , statt createComponent(), new SomeComponent(), getChildren().add()und was nicht. Siehe auch Wie kann ein Snippet einer alten JSP in ein JSF-Äquivalent umgestaltet werden?

Oder, wenn eine Komponente Bedürfnisse auf einem dynamischen Modell basierend „dynamisch wiedergegeben“ werden, dann benutzen Sie einfach eine Iterator Komponente ( <ui:repeat>, <h:dataTable>usw.). Siehe auch So fügen Sie JSF-Komponenten dynamisch hinzu .

Composite-Komponenten sind eine ganz andere Geschichte. Es ist völlig legitim, Komponenten innerhalb von a <cc:implementation>an die Hintergrundkomponente zu binden (dh die durch gekennzeichnete Komponente <cc:interface componentType>. Siehe auch ao java.util.Date über zwei h teilen: inputText-Felder, die Stunde und Minute mit f: convertDateTime darstellen, und wie eine dynamische Liste mit implementiert wird eine JSF 2.0-Verbundkomponente?

Nur bindingim lokalen Bereich verwenden

Manchmal möchten Sie jedoch mehr über den Status einer anderen Komponente innerhalb einer bestimmten Komponente erfahren, mehr als häufig in Anwendungsfällen im Zusammenhang mit der aktions- / wertabhängigen Validierung. Dafür kann das bindingAttribut verwendet werden, jedoch nicht in Kombination mit einer Bean-Eigenschaft. Sie können im bindingAttribut einfach einen eindeutigen Variablennamen im lokalen EL-Bereich angeben , binding="#{foo}"und die Komponente befindet sich während der Renderantwort an einer anderen Stelle in derselben Ansicht direkt als UIComponentReferenz, die von verfügbar ist #{foo}. Hier sind einige verwandte Fragen, bei denen eine solche Lösung in der Antwort verwendet wurde:

Siehe auch:

BalusC
quelle
Ich habe den Benutzer als Bean mit Anforderungsbereich festgelegt. Und versuchte sysout in getlink und setlink Methoden. Bei der Landung auf der Seite = User.User(), User.getLink(), User.setLink(), User.getValue()Wenn ich auf den Link User.User(), User.setLink()...
John
Warum wird setLink () erneut aufgerufen und ein anderes UICommand-Objekt erstellt, wenn es bereits eines hat?
John
"Wenn Sie [...] binden, sollte die Backing Bean absolut nicht in einem breiteren Bereich als dem Anforderungsbereich liegen." Trifft dies in JSF 2.2 noch zu? Was ist die beste Option, wenn ich mit getChildren (). AddAll (...) programmgesteuert Komponenten in die Seite aufnehmen muss?
RinaldoPJr
@Rinaldo: Verwenden Sie JSTL, um die Ansicht programmgesteuert nur in XML-Syntax zu erstellen.
BalusC
1
@ Shirgill: In der Tat. UIViewRoot(alles im UIComponentGrunde) ist sowieso unserialisierbar.
BalusC
1

Jede JSF-Komponente rendert sich selbst in HTML und hat die vollständige Kontrolle darüber, welches HTML sie erzeugt. Es gibt viele Tricks, die von JSF verwendet werden können, und genau, welche dieser Tricks verwendet werden, hängt von der von Ihnen verwendeten JSF-Implementierung ab.

  • Stellen Sie sicher, dass jede Eingabe einen vollständig eindeutigen Namen hat, damit Sie beim erneuten Senden des Formulars an den Komponentenbaum, der es gerendert hat, leicht erkennen können, wo jede Komponente ihr Wertformular lesen kann.
  • Die JSF-Komponente kann Javascript generieren, das an den Serer zurückgesendet wird. Das generierte Javascript weiß auch, wo jede Komponente gebunden ist, da es von der Komponente generiert wurde.
  • Für Dinge wie hlink können Sie Bindungsinformationen als Abfrageparameter oder als Teil der URL selbst oder als matrx-Parameter in die URL aufnehmen. zum Beispiel.

    http:..../somelink?componentId=123würde jsf erlauben, in den Komponentenbaum zu schauen, um zu sehen, dass auf Link 123 geklickt wurde. oder es könnte ehtp:..../jsf;LinkId=123

Die einfachste Möglichkeit, diese Frage zu beantworten, besteht darin, eine JSF-Seite mit nur einem Link zu erstellen und anschließend die von ihr erzeugte HTML-Ausgabe zu untersuchen. Auf diese Weise wissen Sie genau, wie dies mit der von Ihnen verwendeten JSF-Version geschieht.

ams
quelle
Ich würde sagen, dass ich die Komponentenbindung nur verwendet habe, wenn ich die Komponente dynamisch auf der Serverseite generiert habe, alle Attribute wie actionund festgelegt habe valueund dann das JSF-Framework seinen Job machen ließ.
Luiggi Mendoza
Ich habe userals verwaltete Bean mit Anwendungsbereich gespeichert, und wenn ich jedes Mal auf den Link klicke, value="-908991273579182886:-7278326187282654551"ändert sich nur die zweite Zahl , und alles andere ist gleich. Ich frage mich, was diese Magie bewirkt.
John