2010-02-16 8 views
5

Ich arbeite und lerne über JSF + Facelets in diesen Tagen. Ich habe eine BackingBean und eine Facelet xHTML Seite. Wenn ich die Facelet-Seite (nur einmal) anfordere, wird die Backing-Bean-Methode mehrfach aufgerufen.Warum wird die BackingBean-Methode beim Anfordern von Facelet mehrfach aufgerufen?

Was könnte der Grund dafür sein?

Ich kann nichts besonderes sehen. Danke im Voraus. Hier

ist die Facelet:

<?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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<title>Insert title here</title> 
</head> 
<body> 
<ui:composition template="index.xhtml"> 
    <ui:define name="content"> 
     <h:form>Name: <h:inputText id="nameFilterPattern" value="#{kundenBackingBean.nameFilterPattern}" /><h:commandButton value="Suchen"/></h:form> 
     <h:dataTable var="kunde" value="#{kundenBackingBean.kunden}" rowClasses="rowHighlight, rowOrdinary"> 
      <h:column> 
       <f:facet name="header"> 
        <h:outputText value="Kundennr" /> 
       </f:facet> 
       <h:outputText value="#{kunde.kundenNr}"/> 
      </h:column> 
      <h:column> 
       <f:facet name="header"> 
        <h:outputText value="Name" /> 
       </f:facet> 
       <h:outputText value="#{kunde.name}"/> 
      </h:column> 
      <h:column> 
       <f:facet name="header"> 
        <h:outputText value="Vorname" /> 
       </f:facet> 
       <h:outputText value="#{kunde.vorname}"/> 
      </h:column> 
      <h:column> 
       <h:outputLink>Details</h:outputLink> 
      </h:column> 
     </h:dataTable> 
    </ui:define> 
</ui:composition> 
</body> 
</html> 

Und hier ist der Träger-Bohne. Das Verfahren getKunden wird mehrmals aufgerufen:

@ManagedBean 
@SessionScoped 
public class KundenBackingBean extends AbstractBackingBean { 

    private String nameFilterPattern; 

    public List<Kunde> getKunden(){ 
     System.out.println("getKunden"); 
     return getApplication().getKunden(getNameFilterPattern()); 
    } 

    public String getNameFilterPattern() { 
     return nameFilterPattern; 
    } 

    public void setNameFilterPattern(String nameFilterPattern) { 
     System.out.println("Name filter: " + nameFilterPattern); 
     this.nameFilterPattern = nameFilterPattern; 
    } 

} 
+0

wird es immer gleich oft aufgerufen? – volvox

+0

Verwenden Sie Ihren IDE-Debug-Modus, um zu überprüfen, ob diese Methoden mehrmals aufgerufen werden oder ob Sie sie in Protokollen sehen? – Roman

+0

Es scheint so, als würde es bei der ersten Anfrage 8 mal aufgerufen und danach 21 mal aufgerufen.Ich benutze eclipse + glassfish und starte es im Debug-Modus. Ich habe einfach einen Sysout in die Methode eingefügt und die Konsolenausgaben gezählt. – c0d3x

Antwort

8

Die Getter einer Bohne sind nur da Zugriff Modelldaten von der View-Seite. Sie können mehrmals aufgerufen werden. In der Regel ein oder zwei Mal, aber das kann Hunderte von Malen, vor allem wenn auch in UIData Komponenten oder in anderen Attributen als value (wie rendered, disabled, etc.) verwendet werden. Dies schadet normalerweise nicht, da es nur eine einfache Methode ist - Aufruf und Ausführen teurer Logik zum Laden von Daten oder Berechnungen ist normalerweise nicht in den Gettern zu tun. Das Vorladen/Initialisieren ist normalerweise in den Bean-Konstruktor- und/oder -Bohnen-Aktionsmethoden durchzuführen. Getters sollten eigentlich nur die Daten zurückgeben (ggf. auch lazy loading).

Wenn getApplication().getKunden(getNameFilterPattern()); ist eine ziemlich teure Aufgabe tun, sollten Sie es wirklich bewegen entweder an der Bean Konstruktor, oder Bohnen @PostConstruct Methode oder Bohnen Initialisierungsbaustein oder Bohnenaktionsmethode oder einführen verzögertes Laden Muster in dem Getter. Hier ist ein Beispiel dafür, wie dies tun alle:

public class Bean { 
    private String nameFilterPattern; 
    private List<Kunde> kunden; 

    // Load during bean construction. 
    public Bean() { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
    } 

    // OR load during @PostConstruct (will be invoked AFTER construction and resource injection. 
    @PostConstruct 
    public void init() { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
    } 

    // OR during bean initialization (this is invoked BEFORE construction and will apply to ALL constructors). 
    { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
    } 

    // OR during bean action method (invoked from h:commandLink/Button). 
    public String submit() { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
     return "navigationCaseOutcome"; 
    } 

    // OR using lazy loading pattern in getter method. 
    public List<Kunde> getKunden() { 
     if (this.kunden == null) 
      this.kunden = getApplication().getKunden(getNameFilterPattern()); 
     } 
     return this.kunden; 
    } 

In Ihrem speziellen Fall, ich denke, es ist die @PostConstruct (wenn die nameFilterPattern ist von einem GET Anforderungsparameter erhalten werden), oder einfach nur die Bohne Aktionsmethode (wenn nameFilterPattern von einem POST Formular-Eingabefeld erhalten werden soll).

Um mehr über den JSF-Lebenszyklus zu erfahren, finden Sie möglicherweise self-practice article nützlich.

+0

Schöne Antwort, btw :) http://stackoverflow.com/questions/2090033/why-jsf-calls-getters-multiple-times – ewernli

+0

Ja, es ist sicherlich nicht das erste Mal, dass ich antwortete so :) Ich habe es getan mindestens 20 mal vorher. Nicht nur hier, sondern auch auf forums.sun.com und so weiter. – BalusC

2

Es kann aus verschiedenen phases des JSF Lifecylce aufgerufen werden. Meine Wette wäre die Phasen RestoreView und dann RenderResponse - Ich habe JSF in letzter Zeit nicht verwendet, so erinnere ich mich nicht im Detail.

Sie können das neueste Filtermuster und die entsprechenden Clients zwischenspeichern. Sie laden die Clients nur neu, wenn sich der Filter geändert hat. Auf diese Weise lösen Sie dieses spezielle Problem und vermeiden das erneute Laden von Daten, wenn sich der Filter nicht geändert hat.

private String nameFilterPattern; 
private String lastNameFilterPatternLoaded; 
private List<Kunde> clients; 

public List<Kunde> getKunden(){ 
    System.out.println("getKunden"); 
    if(nameFilterPattern.equals(lastNameFilterPatternLoaded)) 
    { 
     clients = getApplication().getKunden(getNameFilterPattern()); 
     lastNameFilterPatternLoaded = nameFilterPattern 
    } 
    return clients; 
} 

Oder Sie können eine request Bohne (statt session) verwenden und stellen Sie sicher, dass Sie die Daten laden nur einmal pro Anfrage.

+0

Danke für die Antwort. Ich habe die Bean geändert, um den Umfang zu beantragen. Das Verhalten ist das gleiche. Wie stelle ich sicher, dass die Daten nur Onces pro Anfrage geladen werden? Wie Sie im Facelet sehen können, wird die Methode nur einmal referenziert. Könnte das mit dem Template-Material zusammenhängen: ui: composition/ui: define? – c0d3x

+0

Die Antwort von BalusC, die Sie angenommen haben, deckt alles ab. Ich schätze, du brauchst keine weitere Erklärung. – ewernli