Jest to spowodowane charakterem odroczonych wyrażeń #{}
(zwróć uwagę, że standardowe wyrażenia „starsze” ${}
zachowują się dokładnie tak samo, gdy zamiast JSP użyto Faceletów). Odroczone wyrażenie nie jest natychmiast analizowane, ale jest tworzone jako ValueExpression
obiekt, a metoda pobierająca wyrażenie jest wykonywana za każdym razem, gdy wywoływany jest kod ValueExpression#getValue()
.
Zwykle będzie to wywoływane jeden lub dwa razy na cykl JSF żądanie-odpowiedź, w zależności od tego, czy składnik jest składnikiem wejściowym czy wyjściowym ( dowiedz się tutaj ). Jednak ta liczba może wzrosnąć (znacznie), gdy jest używana w iteracji komponentów JSF (takich jak <h:dataTable>
i <ui:repeat>
), lub tu i tam w wyrażeniu boolowskim, takim jak rendered
atrybut. JSF (konkretnie EL) w ogóle nie buforuje obliczonego wyniku wyrażenia EL, ponieważ może zwracać różne wartości przy każdym wywołaniu (na przykład, gdy jest zależny od aktualnie iterowanego wiersza danych).
Ocena wyrażenia EL i wywołanie metody gettera to bardzo tania operacja, więc ogólnie nie powinieneś się tym przejmować. Jednak historia zmienia się, gdy z jakiegoś powodu wykonujesz kosztowną logikę DB / biznesową w metodzie gettera. Zostanie to ponownie wykonane za każdym razem!
Getter w JSF podporowych fasoli powinny być zaprojektowane w taki sposób, że tylko powrót już przygotowaną własność i nic więcej, dokładnie jak na specyfikacji JavaBeans . Nie powinny w ogóle robić drogiej logiki DB / biznesowej. W tym celu @PostConstruct
należy zastosować metody detektora komponentu bean i / lub (action). Są one wykonywane tylko raz w pewnym momencie cyklu życia JSF opartego na żądaniach i właśnie tego chcesz.
Oto podsumowanie wszystkich różnych właściwych sposobów wstępnego ustawiania / ładowania właściwości.
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;
}
}
Należy pamiętać, że należy nie używać konstruktora lub bloku inicjalizacji fasoli do pracy, ponieważ może to być wywoływane wielokrotnie jeśli używasz ramy zarządzania fasola, który używa proxy, takie jak CDI.
Jeśli naprawdę nie ma dla ciebie innych sposobów, z powodu pewnych restrykcyjnych wymagań projektowych, powinieneś wprowadzić leniwe ładowanie w metodzie gettera. To znaczy null
, jeśli właściwość jest , a następnie załaduj i przypisz ją do właściwości, w przeciwnym razie zwróć ją.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
W ten sposób kosztowna logika DB / biznes nie będzie niepotrzebnie wykonywana przy każdym wywołaniu gettera.
Zobacz też: