Perché Backup metodo Bean chiamato più volte al momento della richiesta facelet?
Domanda
sto lavorando e di apprendimento su JSF + Facelets in questi giorni. Ho un BackingBean e una pagina facelet xHTML. Quando richiedo il facelet pagine (solo una volta) il-bean-metodo di supporto viene chiamato più volte.
Quale potrebbe essere la ragione di questo?
Non riesco a vedere nulla di speciale. Grazie in anticipo.
Ecco l'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 sostegno 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;
}
}
Soluzione
I getter di un fagiolo sono solo lì per di accesso dati del modello dalla vista laterale. Essi possono essere chiamati più volte. Di solito una o due volte, ma questo può crescere fino a centinaia di volte, soprattutto quando usato in componenti UIData
o in altri attributi rispetto value
(come rendered
, disabled
, ecc). Questo normalmente non pericoloso, in quanto è solo un semplice metodo di invocazione e di fare costosi logica dei dati di carico o calcoli non è solito essere fatto nei getter. Precaricamento / inizializzazione viene solitamente effettuato nel costruttore di fagioli e / o metodi di azione di fagioli. Getters dovrebbe in realtà solo ritorno i dati (se necessario anche fare lazy loading ).
Se getApplication().getKunden(getNameFilterPattern());
sta facendo un compito piuttosto costoso, si dovrebbe davvero spostarlo sia il costruttore di fagioli, o un metodo @PostConstruct
fagioli, o blocco di inizializzazione di fagioli, o metodo di azione di fagioli, o introdurre lazy loading 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 la nameFilterPattern
deve essere ottenuto da un parametro di richiesta GET
), o semplicemente il metodo di azione di fagioli (se nameFilterPattern
deve essere ottenuto da un campo di input modulo POST
) è adatto .
Per saperne di più sul ciclo di vita JSF, si può trovare questo auto- articolo pratica utile.
Altri suggerimenti
Può essere chiamato da diversi fasi del lifecylce JSF. La mia scommessa sarebbe il RestoreView
fasi e poi RenderResponse
- non sono stato utilizzando JSF ultimamente, in modo da non ricordare questo in dettaglio.
È possibile memorizzare nella cache il modello del filtro più recenti e le corrispondenti clienti. Si ricarica il cliente solo se il filtro è cambiato. In questo modo, a risolvere questo particolare problema, oltre a evitare di ricaricare i dati se il filtro non è cambiata.
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;
}
In alternativa è possibile utilizzare un fagiolo request
(invece di session
) e assicuratevi di caricare i dati una sola volta per ogni richiesta.