2010-02-16 15 views
5

Attualmente lavoro e sto imparando su JSF + Facelets. Ho una pagina xHTML BackingBean e Facelet. Quando richiedo la facelet-page (solo una volta) il metodo backing-bean viene chiamato più volte.Perché il metodo BackingBean viene chiamato più volte quando si richiede facelet?

Quale potrebbe essere la ragione di questo?

Non riesco a vedere niente di speciale. Grazie in anticipo.

Ecco la 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> 

E qui è il supporto di soia. Il metodo getKunden viene chiamato più volte:

@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

viene sempre chiamato lo stesso numero di volte? – volvox

+0

usi la tua modalità di debug IDE per verificare i metodi chiamati più volte o lo vedi nei log? – Roman

+0

Sembra che alla prima richiesta venga chiamato 8 volte e dopo viene chiamato 21 volte.Uso eclipse + glassfish e l'ho avviato in modalità di debug. Ho semplicemente inserito un sysout nel metodo e contato le uscite della console. – c0d3x

risposta

8

I getter di un bean sono lì solo per accedere ai dati del modello dal lato della vista. Possono essere chiamati più volte. Di solito una o due volte, ma può crescere fino a centinaia di volte, soprattutto se utilizzato anche nei componenti UIData o in altri attributi rispetto allo value (come rendered, disabled, ecc.). Questo di solito non fa male, in quanto è solo una semplice invocazione di metodo e una costosa logica di caricamento dei dati o calcoli di solito non deve essere fatto nei getter. Il precaricamento/inizializzazione di solito deve essere fatto nei metodi di costruzione bean e/o bean. Getters dovrebbe infatti solo restituire i dati (se necessario, fare anche pigro carico).

Se getApplication().getKunden(getNameFilterPattern()); sta facendo un compito piuttosto costoso, si dovrebbe davvero spostarlo sia il costruttore di fagioli, o un metodo di fagioli @PostConstruct, o blocco di inizializzazione di fagioli, o metodo di azione di fagioli, o introdurre caricamento pigro modello nel getter. Ecco un esempio che mostra come fare tutto questo:

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; 
    } 

Nel vostro caso specifico, penso che sia il @PostConstruct (se il nameFilterPattern deve essere ottenuto da un parametro GET richiesta), o semplicemente il metodo di azione di fagioli (se è necessario ottenere nameFilterPattern da un campo di immissione modulo POST).

Per ulteriori informazioni sul ciclo di vita JSF, è possibile trovare utile questo self-practice article.

+0

Bella risposta, btw :) http://stackoverflow.com/questions/2090033/why-jsf-calls-getters-multiple-times – ewernli

+0

Sì, non è certo la prima volta che ho risposto così :) L'ho interrotto almeno 20 volte prima. Non solo qui, ma anche su forums.sun.com e così via. – BalusC

2

E possono essere richiamati dalla diversa phases del lifecylce JSF. La mia scommessa sarebbe le fasi RestoreView e poi RenderResponse - Non ho usato JSF ultimamente, quindi non ricordo questo in dettaglio.

È possibile memorizzare nella cache l'ultimo modello di filtro ei client corrispondenti. Ricarichi i client solo se il filtro è cambiato. In questo modo, risolvi questo particolare problema, inoltre evita di ricaricare i dati se il filtro non cambia.

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; 
} 

oppure è possibile utilizzare un request fagioli (invece di session) e assicuratevi di caricare i dati una sola volta per ogni richiesta.

+0

Grazie per aver risposto. Ho cambiato il bean per richiedere scope. Il comportamento è lo stesso. Come faccio a essere sicuro di caricare i dati solo per richiesta? Come puoi vedere nel facelet, il metodo viene riferito una sola volta. Potrebbe questo isse essere correlato al modello: ui: composition/ui: define? – c0d3x

+0

La risposta di BalusC che hai accettato copre tutto. Immagino tu non abbia bisogno di ulteriori spiegazioni. – ewernli

Problemi correlati