Як працює атрибут 'binding' у JSF? Коли і як його слід застосовувати?


78

Є багато матеріалів, що розрізняють 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's 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тут зберігається?

Відповіді:


132

Як це працює?

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

В результаті він пов'язує посилання на екземпляр компонента в дереві компонентів із змінною області дії. Ця інформація жодним чином не видно у згенерованому HTML-представленні самого компонента. Ця інформація в жодному разі не має відношення до згенерованого виводу HTML. Коли форма подається і подання відновлюється, дерево компонентів JSF просто відновлюється з нуля, і всі bindingатрибути будуть просто переоцінені, як описано вище у параграфі. Після відтворення дерева компонентів JSF відновить стан перегляду JSF у дереві компонентів.

Екземпляри компонентів мають масштаб запиту!

Важливо знати і розуміти, що конкретні екземпляри компонентів ефективно вимагають масштабування. Вони створюються щоразу за кожним запитом, а їх властивості заповнюються значеннями стану JSF на етапі перегляду відновлення. Отже, якщо ви прив'язуєте компонент до властивості резервного компонента, тоді резервний компонент абсолютно не повинен знаходитись у ширшому обсязі, ніж область запиту. Див. Також специфікацію JSF 2.0, розділ 3.1.5:

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

...

Прив'язки компонентів часто використовуються разом із JavaBeans, які динамічно створюються за допомогою засобу створення керованого компонента (див. Розділ 5.8.1 "VariableResolver і VariableResolver за замовчуванням"). Наполегливо рекомендується розробникам додатків розміщувати керовані компоненти, на які вказують вирази прив'язки компонентів, у область "запит". Це пов’язано з тим, що розміщення його в сесії або області застосування вимагатиме захисту потоків, оскільки екземпляри UIComponent залежать від запуску всередині одного потоку. Також є потенційно негативні наслідки для управління пам’яттю при розміщенні прив’язки компонентів до “сеансу”.

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

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

Використання bindingвластивості квасолі - погана практика

Незважаючи на те, що використання bindingцього способу прив'язка цілого екземпляра компонента до властивості bean, навіть за запитом з обсягом bean, є у JSF 2.xa досить рідкісним випадком використання і, як правило, не найкращою практикою. Це вказує на дизайнерський запах. Ви нормально оголошуєте компоненти осторонь зору і зв'язати їх виконання атрибутів , як valueі , можливо , інші , як styleClass, disabled, renderedі т.д., для нормальних властивостей компонента. Потім ви просто маніпулюєте саме тим властивістю bean, яке хочете, замість того, щоб захопити весь компонент і викликати метод setter, пов'язаний з атрибутом.

У тих випадках , коли компонент повинен бути «динамічно побудована» на основі статичної моделі, краще використовувати час перегляду збірки теги , як JSTL , якщо це необхідно в файлі тегів , замість того createComponent(), new SomeComponent(), getChildren().add()а що ні. Див. Також Як рефакторувати фрагмент старого JSP до якогось еквівалента JSF?

Або, якщо компонент повинен бути «динамічно виявляється» на основі динамічної моделі, а потім просто використовувати компонент ітератора ( <ui:repeat>, <h:dataTable>і т.д.). Див. Також Як динамічно додавати компоненти JSF .

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

Використовувати лише bindingу місцевому масштабі

Однак іноді вам хочеться дізнатись про стан іншого компонента зсередини певного компонента, частіше у випадках використання, пов’язаних із валідацією, залежною від дії / значення. Для цього bindingможна використовувати атрибут, але не в поєднанні з властивістю bean. Ви можете просто вказати унікальне ім'я змінної в локальній області EL в bindingатрибуті так, binding="#{foo}"і компонент знаходиться під час відповіді на візуалізацію в іншому місці в тому ж поданні, як UIComponentпосилання, доступне #{foo}. Ось кілька супутніх питань, де у відповіді було використано таке рішення:

Дивитися також:


Я встановив користувача як бін із масштабом запиту. І спробував sysout у методах getlink та setlink. При посадці на сторінку = User.User(), User.getLink(), User.setLink(), User.getValue()Коли я натискаю посилання =User.User(), User.setLink()...
Джон

Чому setLink () викликається знову і створюється інший об’єкт UICommand, коли він уже є?
Джон

"якщо ви прив'язуєте [...], тоді допоміжний компонент абсолютно не повинен знаходитись у ширшій області, ніж область запиту". Чи все ще це правда в JSF 2.2? Який найкращий варіант, якщо мені доводиться програмно включати компоненти на сторінку за допомогою getChildren (). AddAll (...)?
RinaldoPJr,

@Rinaldo: Використовуйте JSTL для програмної побудови подання лише у синтаксисі XML.
BalusC

1
@ Ширгілл: Справді. UIViewRoot(все в UIComponentосновному) в будь-якому випадку несеризується.
BalusC

1

кожен компонент JSF видає себе в HTML і має повний контроль над тим, який HTML він створює. Є багато прийомів, які може використовувати JSF, і який саме з цих прийомів буде використаний, залежить від реалізації JSF, яку ви використовуєте.

  • Переконайтеся, що кожен з вхідних даних має повністю унікальне ім'я, щоб, коли форма повертається до дерева компонентів, яке її відтворило, було легко визначити, де кожен компонент може прочитати свою форму значення.
  • Компонент JSF може генерувати javascript, який надсилається назад користувачеві, згенерований javascript знає, куди прив'язаний кожен компонент, оскільки його генерував компонент.
  • Для таких речей, як hlink, ви можете включити інформацію про прив'язку до URL-адреси як параметри запиту або як частину самої URL-адреси або як параметри matrx. для прикладів.

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

Найпростіший спосіб відповісти на це питання - створити JSF-сторінку з одним посиланням, а потім перевірити вихідний файл html, який він створює. Таким чином ви точно будете знати, як це відбувається, використовуючи версію JSF, яку ви використовуєте.


Я б сказав, що я використовував прив'язку компонентів лише тоді, коли динамічно генерував компонент на стороні сервера, встановлюючи всі атрибути як actionі value, а потім нехай JSF-фреймворк робить свою роботу.
Луїджі Мендоса

Я зберігав userяк керований компонент додатка, і коли я натискаю посилання щоразу, value="-908991273579182886:-7278326187282654551"змінюється лише друге число, а все інше однакове. Цікаво, яку магію це робить.
Джон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.