Це викликано характером відкладених виразів #{}
(зауважте, що "застарілі" стандартні вирази ${}
поводяться точно так само, коли Facelets використовується замість JSP). Відкладений вираз не оцінюється відразу , а створюється як ValueExpression
об'єкт, і метод getter за виразом виконується щоразу, коли код викликає ValueExpression#getValue()
.
Зазвичай це буде викликано один або два рази за цикл відповіді на запит JSF, залежно від того, компонент є вхідним чи вихідним компонентом ( дізнайтеся це тут ). Однак ця кількість може зрости (набагато) вище при використанні в ітерації компонентів JSF (таких як <h:dataTable>
і <ui:repeat>
) або тут і там в булевому виразі, як rendered
атрибут. JSF (конкретно, EL) взагалі не кешуватиме оцінений результат вираження EL, оскільки він може повертати різні значення для кожного виклику (наприклад, коли це залежить від поточно ітералізованого рядка даних).
Оцінка виразу EL та використання методу getter - це дуже дешева операція, тому взагалі про це не варто хвилюватися. Однак історія змінюється, коли ви використовуєте дорогу логіку DB / бізнесу в методі getter чомусь. Це буде повторно виконуватися кожного разу!
Методи Getter в резервних бобах JSF повинні бути розроблені таким чином, щоб вони повертали виключно вже підготовлену властивість і нічого більше, точно відповідно до специфікації Javabeans . Вони взагалі не повинні робити жодної дорогої логіки БД / бізнесу. Для цього @PostConstruct
слід використовувати методи прослуховування фасолі та / або (дії). Вони виконуються лише один раз у якийсь момент життєвого циклу JSF на основі запиту, і саме це ви хочете.
Ось короткий опис усіх правильних способів попередньо встановити / завантажити об’єкт.
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;
}
}
Зверніть увагу , що ви повинні НЕ використовувати конструктор або ініціалізацію блоку бін для роботи , тому що він може бути викликаний кілька разів , якщо ви використовуєте систему управління бобом , який використовує проксі, такі як CDI.
Якщо для вас дійсно немає інших способів, через деякі обмежувальні вимоги до дизайну, тоді вам слід ввести ледаче завантаження всередині методу геттера. Тобто, якщо властивість є null
, то завантажте і призначте її, а інше поверніть її.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
Таким чином, дорога логіка БД / бізнесу не буде зайвою необхідністю виконуватись при кожному виклику доступу.
Дивитися також: