Dies wird durch die Art der verzögerten Ausdrücke verursacht #{}
(beachten Sie, dass sich "Legacy" -Standardausdrücke ${}
genau gleich verhalten, wenn Facelets anstelle von JSP verwendet wird). Der verzögerte Ausdruck wird nicht sofort ausgewertet, sondern als ValueExpression
Objekt erstellt, und die Getter-Methode hinter dem Ausdruck wird jedes Mal ausgeführt, wenn der Code aufgerufen wirdValueExpression#getValue()
.
Dies wird normalerweise ein- oder zweimal pro JSF-Anforderungs- / Antwortzyklus aufgerufen, je nachdem, ob die Komponente eine Eingabe- oder Ausgabekomponente ist ( lernen Sie es hier ). Diese Anzahl kann jedoch (viel) höher sein, wenn sie beim Iterieren von JSF-Komponenten (wie <h:dataTable>
und <ui:repeat>
) oder hier und da in einem booleschen Ausdruck wie dem rendered
Attribut verwendet wird. JSF (insbesondere EL) speichert das ausgewertete Ergebnis des EL-Ausdrucks überhaupt nicht zwischen, da es bei jedem Aufruf unterschiedliche Werte zurückgeben kann (z. B. wenn es von der aktuell iterierten datierbaren Zeile abhängt).
Das Auswerten eines EL-Ausdrucks und das Aufrufen einer Getter-Methode ist eine sehr kostengünstige Operation, daher sollten Sie sich darüber im Allgemeinen überhaupt keine Gedanken machen. Die Story ändert sich jedoch, wenn Sie aus irgendeinem Grund teure DB- / Geschäftslogik in der Getter-Methode ausführen. Dies würde jedes Mal neu ausgeführt!
Getter - Methoden in JSF Träger Bohnen sollten auf diese Weise gestaltet werden , dass sie nur zurückgeben , die bereits vorbereitete Eigenschaft und nichts mehr, genau wie pro die Javabeans - Spezifikation . Sie sollten überhaupt keine teure DB- / Geschäftslogik ausführen. Dazu @PostConstruct
sollten die Listener-Methoden der Bean und / oder (Aktion) verwendet werden. Sie werden zu einem bestimmten Zeitpunkt des anforderungsbasierten JSF-Lebenszyklus nur einmal ausgeführt , und genau das möchten Sie.
Hier finden Sie eine Zusammenfassung aller verschiedenen richtigen Methoden zum Voreinstellen / Laden einer Eigenschaft.
public class Bean {
private SomeObject someProperty;
@PostConstruct
public void init() {
// In @PostConstruct (will be invoked immediately after construction and dependency/property injection).
someProperty = loadSomeProperty();
}
public void onload() {
// Or in GET action method (e.g. <f:viewAction action>).
someProperty = loadSomeProperty();
}
public void preRender(ComponentSystemEvent event) {
// Or in some SystemEvent method (e.g. <f:event type="preRenderView">).
someProperty = loadSomeProperty();
}
public void change(ValueChangeEvent event) {
// Or in some FacesEvent method (e.g. <h:inputXxx valueChangeListener>).
someProperty = loadSomeProperty();
}
public void ajaxListener(AjaxBehaviorEvent event) {
// Or in some BehaviorEvent method (e.g. <f:ajax listener>).
someProperty = loadSomeProperty();
}
public void actionListener(ActionEvent event) {
// Or in some ActionEvent method (e.g. <h:commandXxx actionListener>).
someProperty = loadSomeProperty();
}
public String submit() {
// Or in POST action method (e.g. <h:commandXxx action>).
someProperty = loadSomeProperty();
return "outcome";
}
public SomeObject getSomeProperty() {
// Just keep getter untouched. It isn't intented to do business logic!
return someProperty;
}
}
Beachten Sie, dass Sie den Bean-Konstruktor oder den Initialisierungsblock für den Job nicht verwenden sollten, da er möglicherweise mehrmals aufgerufen wird, wenn Sie ein Bean-Verwaltungsframework verwenden, das Proxys wie CDI verwendet.
Wenn es für Sie aufgrund einiger restriktiver Designanforderungen wirklich keine anderen Möglichkeiten gibt, sollten Sie das verzögerte Laden in die Getter-Methode einführen. Das heißt, wenn die Eigenschaft ist null
, dann laden Sie sie und weisen Sie sie der Eigenschaft zu, andernfalls geben Sie sie zurück.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
Auf diese Weise wird die teure DB / Business-Logik nicht unnötig bei jedem einzelnen Getter-Aufruf ausgeführt.
Siehe auch:
FacesContext#getCurrentPhaseId()
.Mit JSF 2.0 können Sie einen Listener an ein Systemereignis anhängen
Alternativ können Sie die JSF-Seite in ein
f:view
Tag einschließenquelle
Ich habe einen Artikel darüber geschrieben, wie man JSF-Beans-Getter mit Spring AOP zwischenspeichert.
Ich erstelle eine einfache,
MethodInterceptor
die alle Methoden abfängt, die mit einer speziellen Anmerkung versehen sind:Dieser Interceptor wird in einer Federkonfigurationsdatei verwendet:
Hoffe es wird helfen!
quelle
Ursprünglich veröffentlicht im PrimeFaces-Forum unter http://forum.primefaces.org/viewtopic.php?f=3&t=29546
Vor kurzem war ich besessen davon, die Leistung meiner App zu bewerten, JPA-Abfragen zu optimieren, dynamische SQL-Abfragen durch benannte Abfragen zu ersetzen, und erst heute Morgen erkannte ich, dass eine Getter-Methode in Java Visual VM eher ein HOT SPOT war als der Rest von mein Code (oder der Großteil meines Codes).
Getter-Methode:
Referenziert von ui: in in index.xhtml aufnehmen
Unten sehen Sie, dass PageNavigationController.getGmapsAutoComplete () ein HOT SPOT (Leistungsproblem) in Java Visual VM ist. Wenn Sie weiter unten auf dem Bildschirm nachsehen, werden Sie feststellen, dass getLazyModel (), die Lazy Datatable Getter-Methode von PrimeFaces, ebenfalls ein Hot Spot ist, nur wenn der Endbenutzer viele "Lazy Datatable" -Typen / Operationen / Aufgaben ausführt in der App. :) :)
Siehe (Original-) Code unten.
In index.xhtml wird auf Folgendes verwiesen:
Lösung: Da es sich um eine Getter-Methode handelt, verschieben Sie den Code und weisen Sie gmapsAutoComplete einen Wert zu, bevor Sie die Methode aufrufen. siehe Code unten.
Testergebnisse: PageNavigationController.getGmapsAutoComplete () ist in Java Visual VM kein HOT SPOT mehr (wird nicht einmal mehr angezeigt)
Teilen Sie dieses Thema, da viele der erfahrenen Benutzer Junior-JSF-Entwicklern geraten haben, KEINEN Code in 'Getter'-Methoden hinzuzufügen. :) :)
quelle
Wenn Sie CDI verwenden, können Sie Producers-Methoden verwenden. Es wird viele Male aufgerufen, aber das Ergebnis des ersten Aufrufs wird im Bereich der Bean zwischengespeichert und ist effizient für Getter, die schwere Objekte berechnen oder initialisieren! Sehen Sie hier , für weitere Informationen.
quelle
Sie könnten wahrscheinlich AOP verwenden, um eine Art Aspekt zu erstellen, der die Ergebnisse unserer Getter für einen konfigurierbaren Zeitraum zwischengespeichert. Dies würde verhindern, dass Sie Boilerplate-Code in Dutzenden von Accessoren kopieren und einfügen müssen.
quelle
Dies nennen wir eine vorzeitige Optimierung. In dem seltenen Fall, dass ein Profiler Ihnen mitteilt, dass die Berechnung einer Eigenschaft so außerordentlich teuer ist, dass ein dreimaliger und kein einmaliger Aufruf erhebliche Auswirkungen auf die Leistung hat, fügen Sie wie beschrieben Caching hinzu. Aber wenn Sie nicht etwas wirklich Dummes tun, wie das Faktorisieren von Primzahlen oder den Zugriff auf eine Datenbank in einem Getter, weist Ihr Code höchstwahrscheinlich ein Dutzend schlimmerer Ineffizienzen an Stellen auf, an die Sie noch nie gedacht haben.
quelle
Ich würde auch raten, ein solches Framework wie Primefaces anstelle von JSF zu verwenden. Sie behandeln solche Probleme vor dem JSF-Team e. g In Primefaces können Sie die teilweise Übermittlung festlegen. Ansonsten hat BalusC es gut erklärt.
quelle
Es ist immer noch ein großes Problem in JSF. Zum Beispiel, wenn Sie eine Methode haben
isPermittedToBlaBla
für Sicherheitsüberprüfungen haben und Ihrer Ansicht nach habenrendered="#{bean.isPermittedToBlaBla}
wird die Methode mehrmals aufgerufen.Die Sicherheitskontrolle könnte z. LDAP-Abfrage usw. Sie müssen dies also mit vermeiden
und Sie müssen dies innerhalb einer Session Bean pro Anfrage sicherstellen.
Ich denke, JSF muss hier einige Erweiterungen implementieren, um mehrere Aufrufe zu vermeiden (z. B. Annotation ruft
@Phase(RENDER_RESPONSE)
diese Methode nur einmal nach derRENDER_RESPONSE
Phase auf ...)quelle