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