Почему JSF называет методы считывания многократно

Скажем, я указываю outputText компонент как это:

<h:outputText value="#{ManagedBean.someProperty}"/>

Если я печатаю сообщение журнала когда метод считывания для someProperty назван и загрузите страницу, это тривиально, чтобы заметить, что метод считывания называют несколько раз на запрос (дважды, или три раза то, что произошло в моем случае):

DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property

Если значение someProperty является дорогим для вычисления, это может потенциально быть проблемой.

Я погуглил немного и полагал, что это - известная проблема. Одно обходное решение должно было включать проверку и видеть, было ли оно уже вычислено:

private String someProperty;

public String getSomeProperty() {
    if (this.someProperty == null) {
        this.someProperty = this.calculatePropertyValue();
    }
    return this.someProperty;
}

Основная проблема с этим состоит в том, что Вы получаете загрузки шаблонного кода, не говоря уже о частных переменных, в которых Вы, возможно, не нуждались бы.

Что альтернативы к этому подходу? Существует ли способ достигнуть этого без такого большого количества ненужного кода? Существует ли способ мешать JSF вести себя таким образом?

Спасибо за Ваш вход!

253
задан BalusC 2 March 2016 в 12:39
поделиться

3 ответа

Это вызвано природой отложенных выражений # {} (обратите внимание, что "устаревшие" стандартные выражения $ {} ведут себя точно так же, когда Facelets используется вместо JSP). Отложенное выражение не вычисляется немедленно , а создается как объект ValueExpression , и метод получения, стоящий за выражением, выполняется каждый раз, когда код вызывает ValueExpression # getValue () .

Обычно это вызывается один или два раза за цикл запрос-ответ JSF, в зависимости от того, является ли компонент входным или выходным компонентом ( узнайте об этом здесь ).Однако это количество может возрасти (намного) выше при использовании в итерации компонентов JSF (таких как и ) или здесь и там в логическое выражение, подобное атрибуту rendered . JSF (в частности, EL) вообще не кэширует оцененный результат выражения EL, поскольку он может возвращать разные значения при каждом вызове (например, когда он зависит от текущей итерируемой строки данных).

Вычисление выражения EL и вызов метода получения - очень дешевая операция, поэтому вам вообще не следует беспокоиться об этом. Однако ситуация меняется, когда по какой-то причине вы выполняете дорогостоящую БД / бизнес-логику в методе получения. Это будет каждый раз повторяться!

Getter-методы в поддерживающих JSF-компонентах должны быть спроектированы таким образом, чтобы они возвращали только уже подготовленное свойство и ничего более, точно в соответствии со спецификацией Javabeans . Они вообще не должны использовать дорогостоящую БД / бизнес-логику. Для этого следует использовать методы bean-компонента @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;
    }

}

Обратите внимание, что вам не следует не использовать конструктор bean-компонента или блок инициализации для задания, потому что он может быть вызван несколько раз, если вы используете структуру управления bean-компонентами, которая использует прокси, например CDI.

Если у вас действительно нет других способов из-за некоторых ограничительных требований к дизайну, вам следует ввести ленивую загрузку внутри метода получения. Т.е. если свойство равно null , то загрузите и назначьте его свойству, иначе верните его.

    public SomeObject getSomeProperty() {
        // If there are really no other ways, introduce lazy loading.
        if (someProperty == null) {
            someProperty = loadSomeProperty();
        }

        return someProperty;
    }

Таким образом, дорогостоящая БД / бизнес-логика не будет без необходимости выполняться при каждом вызове геттера.

См. Также:

337
ответ дан 23 November 2019 в 02:51
поделиться

Вы, вероятно, можете использовать AOP для создания какого-либо аспекта, который кэшировал результаты наших получений на настраиваемое количество времени. Это помешает вам потребоваться для копирования и пастового кода котельной в десятки доступа.

3
ответ дан 23 November 2019 в 02:51
поделиться

Если значение какой-либо трудоустройства дорогой для расчета, это может потенциально быть проблемой.

Это то, что мы называем преждевременную оптимизацию. В редких случаях о том, что профилировщик говорит вам, что расчет имущества настолько чрезвычайно дороги, который призывает его в три раза, а не только один раз имеет значительное влияние на производительность, вы добавляете кэширование, как вы описываете. Но если вы не сделаете что-то действительно глупое, как факторинг простых чисел или доступа к бамбусу в Getter, ваш код, скорее всего, имеет дюжину худших неэффективности в местах, о которых вы никогда не думали.

-1
ответ дан 23 November 2019 в 02:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: