Как работает атрибут привязки в JSF? Когда и как его использовать?

В JSF существует много материалов, различающих атрибут value и атрибут binding.

Мне интересно, как оба подхода отличаются друг от друга. Дано:

public class User {
    private String name;
    private UICommand link;

    // Getters and setters omitted.
}
<h:form>
    <h:commandLink binding="#{user.link}" value="#{user.name}" />
</h:form>

Довольно прямо, что происходит, когда указан атрибут value. Геттер запускается, чтобы вернуть значение свойства name для User bean. Значение выводится на вывод HTML.

Но я не мог понять, как работает binding. Как сгенерированный HTML поддерживает привязку к свойству link User bean?

Ниже приведена соответствующая часть сгенерированного вывода после ручной сортировки и комментариев (обратите внимание, что идентификатор j_id_jsp_1847466274_1 был автоматически сгенерирован и что есть два скрытых входных виджета). Я использую Sun JSF RI, версия 1.2.

<form action="/TestJSF/main.jsf" enctype="application/x-www-form-urlencoded"
    id="j_id_jsp_1847466274_1" method="post"  name="j_id_jsp_1847466274_1">
    <input name="j_id_jsp_1847466274_1" type="hidden" value="j_id_jsp_1847466274_1">
    <a href="#" onclick="...">Name</a>
    <input autocomplete="off" id="javax.faces.ViewState" name="javax.faces.ViewState"
        type="hidden" value="-908991273579182886:-7278326187282654551">
</form>

Где хранится binding?

Ответ 1

Как это работает?

При создании/восстановлении представления JSF (Facelets/JSP file) будет создано дерево компонентов JSF. В этот момент время сборки просмотров оцениваются все атрибуты binding (вместе с id атрибутами и обработчиками меток, такими как JSTL). Когда компонент JSF должен быть создан до его добавления в дерево компонентов, JSF проверяет, возвращает ли атрибут binding предварительно обработанный компонент (т.е. Не null), и если да, то используйте его. Если он не будет создан, то JSF автоматически создаст компонент "обычный способ" и вызовет сеттер за атрибутом binding с экземпляром autocreated component в качестве аргумента.

В эффектах он связывает ссылку экземпляра компонента в дереве компонентов с областью переменной. Эта информация никоим образом не отображается в сгенерированном представлении HTML самого компонента. Эта информация никоим образом не связана с генерируемым выходом HTML. Когда форма отправляется и представление восстанавливается, дерево компонентов JSF просто перестраивается с нуля, и все атрибуты binding просто переоцениваются, как описано в предыдущем абзаце. После воссоздания дерева компонентов JSF восстановит состояние представления JSF в дереве компонентов.

Экземпляры компонентов - это область с запросом!

Важно знать и понимать, что конкретные экземпляры компонентов эффективно запрашивают область действия. Они создаются по каждому запросу, и их свойства заполняются значениями из состояния просмотра JSF во время фазы просмотра. Итак, если вы привязываете компонент к свойству поддержки bean, то поддержка bean должна абсолютно не находиться в более широкой области, чем область запроса. См. Также Спецификация JSF 2.0 глава 3.1.5:

3.1.5 Компонентные привязки

...

Связывание компонентов часто используется в сочетании с JavaBeans, которые динамически создаются с помощью управляемых bean Объект создания (см. раздел 5.8.1 "VariableResolver и Default VariableResolver" ). Это сильно рекомендуется, чтобы разработчики приложений размещали управляемые beans, на которые указывали выражения привязки компонентов в "запрос". Это связано с тем, что размещение его в сеансе или области приложения потребует безопасности потоков, поскольку Экземпляры UIComponent зависят от запуска внутри одного потока. Возможны также негативные последствия для управления памятью при размещении привязки компонента в области "сеанс".

В противном случае экземпляры компонентов распределяются между несколькими запросами, что может привести к ошибкам "дублировать идентификатор компонента и" странным" поведением, поскольку валидаторы, преобразователи и слушатели, объявленные в вид повторно присоединяются к существующему экземпляру компонента из предыдущего запроса (-ов). Симптомы ясны: они выполняются несколько раз, один раз с каждым запросом в той же области, в которой был связан компонент.

И при большой нагрузке (т.е. когда несколько разных HTTP-запросов (потоков) одновременно обрабатывают и обрабатывают один и тот же экземпляр компонента), вы можете столкнуться рано или поздно с крахом приложения, например. Застрял поток в UIComponent.popComponentFromEL или Java Threads при 100% использовании ЦП с использованием богатых интерфейсов UIDataAdaptorBase и его внутренней HashMap, или даже некоторые "странные" IndexOutOfBoundsException или ConcurrentModificationException, идущие прямо из исходного кода реализации JSF, в то время как JSF занят сохранением или восстановлением состояния представления (т.е. трассировка стека показывает методы saveState() или restoreState() и т.п.).

Использование binding в bean - это плохая практика

Независимо от этого, используя binding таким образом, привязка всего экземпляра компонента к свойству bean, даже в области с привязкой к запросу bean, находится в JSF 2.x довольно редкий случай использования и, как правило, не самая лучшая практика. Это указывает на запах дизайна. Обычно вы объявляете компоненты на стороне представления и связываете их атрибуты времени выполнения, такие как value и, возможно, другие, такие как styleClass, disabled, rendered и т.д., С обычными свойствами bean. Затем вы просто управляете именно этим свойством bean, а не захватываете весь компонент и вызываете метод setter, связанный с этим атрибутом.

В случаях, когда компонент должен быть "динамически построен" на основе статической модели, лучше использовать просмотр тегов времени построения, таких как JSTL, при необходимости в файл тега, вместо createComponent(), new SomeComponent(), getChildren().add(), а что нет. См. Также Как отредактировать фрагмент старого JSP на эквивалент JSF?

Или, если компонент должен быть "динамически визуализирован" на основе динамической модели, просто используйте компонент итератора (<ui:repeat>, <h:dataTable>, и т.д). См. Также Как динамически добавлять компоненты JSF.

Составные компоненты - совершенно другая история. Полностью законно связывать компоненты внутри <cc:implementation> с компонентом поддержки (т.е. Компонентом, идентифицированным <cc:interface componentType>. См. Также ao Разделить java.util.Date на два h: поля inputText представляя час и минуту с помощью f: convertDateTime и Как реализовать динамический список с составным компонентом JSF 2.0?

Используйте только binding в локальной области

Однако иногда вы хотите узнать о состоянии другого компонента изнутри определенного компонента, чаще, чем часто в случаях использования, связанных с проверкой на действие/значение. Для этого можно использовать атрибут binding, но не в сочетании с свойством bean. Вы можете указать только имя локальной переменной локального EL-области в атрибуте binding, например, binding="#{foo}", и этот компонент находится во время отклика в другом месте в том же представлении, что и ссылка UIComponent, доступная #{foo}. Вот несколько связанных вопросов, в которых такое решение используется в ответе:

См. также:

Ответ 2

каждый компонент JSF отображается в HTML и имеет полный контроль над тем, что он производит. Существует много трюков, которые могут быть использованы JSF, и точно, какой из этих трюков будет использоваться, зависит от используемой вами реализации JSF.

  • Убедитесь, что каждый из входных данных имеет полное уникальное имя, поэтому, когда форма отправляется обратно в дерево компонентов, которое ее отображает, легко определить, где каждый компонент может прочитать свою форму значения.
  • Компонент JSF может генерировать javascript, который отправляется обратно в serer, сгенерированный javascript знает, где тоже связан каждый компонент, потому что он был сгенерирован компонентом.
  • Для таких вещей, как hlink, вы можете включить информацию привязки в url как параметры запроса или как часть самого URL или как параметры matrx. Например.

    http:..../somelink?componentId=123 позволит jsf посмотреть в дереве компонентов, чтобы увидеть, что была нажата ссылка 123. или он мог бы htp:..../jsf;LinkId=123

Самый простой способ ответить на этот вопрос - создать страницу JSF только с одной ссылкой, а затем изучить вывод html, который он производит. Таким образом вы точно узнаете, как это происходит, используя версию JSF, которую вы используете.