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

Есть много материалов, дифференцирующих value атрибут и binding атрибут в JSF.

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

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 боб. Значение выводится в вывод HTML.

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

Ниже приведена соответствующая часть сгенерированного вывода после ручного украшения и комментирования (обратите внимание, что идентификатор 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 хранится здесь?

2 ответа

Решение

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

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

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

Экземпляры компонентов находятся в области запроса!

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

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

...

Привязки компонентов часто используются вместе с JavaBean-компонентами, которые динамически создаются с помощью средства создания управляемых компонентов (см. Раздел 5.8.1 "VariableResolver и VariableResolver по умолчанию"). Настоятельно рекомендуется, чтобы разработчики приложений помещали управляемые bean-компоненты, на которые указывают выражения привязки компонентов, в область "запроса". Это связано с тем, что для помещения его в сессию или область приложения потребуется безопасность потоков, поскольку экземпляры UIComponent зависят от работы внутри одного потока. Также возможно негативное влияние на управление памятью при помещении привязки компонента в область "сеанса".

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

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

С помощью binding на свойствах бобов - плохая практика

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

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

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

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

Использовать только binding в местном масштабе

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

Смотрите также:

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

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

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

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

Другие вопросы по тегам