Скачиваний:
88
Добавлен:
09.05.2014
Размер:
823.27 Кб
Скачать

Данное руководство посвящено введению в технологию JavaServer Faces (JSF) — серверную компонентную инфраструктуру, облегчающую разработку интерфейсов для Web-приложений на Java. Оно в первую очередь будет полезно для разработчиков, не имеющих опыта применения JSF и желающих максимально быстро начать использовать ее преимущества, в первую очередь, компоненты, снижающие затраты на разработку интерфейсов. В руководстве рассматриваются только основные моменты, причем они поясняются на многочисленных примерах.

JSF близка к традиционным технологиям разработки интерфейсов, таким как AWT, SWT и Swing. Одним из ее основных преимуществ является то, что программист освобождается от черновой работы, которая ложится на плечи разработчиков самой инфраструктуры JSF. Из-за этого сама JSF оказывается сложнее многих других Web-технологий, но ее внутренняя структура скрыта от глаз разработчиков приложений. Создавать Web-приложения на основе JSF как правило легче, чем с помощью других каркасов: они получаются короче, а также проще в смысле структуры и конфигурирования.

Если вы занимаетесь разработкой серверных Web-приложений, то изучить JSF будет проще остальных подобных технологий. Она специально проектировалась с прицелом на Web-приложения (а не Web-сайты). Благодаря JSF вы можете сконцентрироваться на разработке непосредственно логики приложения и не заботиться о низкоуровневой работе с объектами и параметрами запросов, сессиями, а также о проблемах обработки сложных XML-документов. Используя JSF, вы достигнете больших результатов за меньшее время, чем при использовании других Java-технологий для Webразработки.

Подобно Swing и AWT, JSF представляет собой каркас разработки приложений, предоставляющий набор стандартных графических компонентов для создания интерфейсов. Но при этом JSF ориентирована на создание Web-приложений и имеет следующие преимущества:

Четкое разделение бизнес-логики и интерфейса

Управление сохраняемостью на уровне компонент

Простая работа с событиями на стороне сервера

Использование простых и знакомых концепций, таких как графический интерфейс или слои в Web-приложении

Доступность нескольких реализаций от различных компаний-разработчиков

Широкая поддержка со стороны интегрированных средств разработки (IDE)

Как правило, приложение, созданное на основе JSF, состоит из следующих частей:

Объектов JavaBean, управляющих состоянием и поведением приложения

Компонентов GUI, с возможностью сохранения состояния

Классов, реализующих событийную модель, например, слушателей (listeners), аналогичных тем, что используются при традиционной разработке графических интерфейсов

Страниц, выступающих в роли представлений в парадигме Модель-Представление-Контроллер (Model-View- Controller - MVC). Подобные страницы могут обращаться к корневым узлам представления (view roots) через

дерево компонентов JSF.

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

Компонентная архитектура

Компоненты и их состояния

Зачастую наибольшей трудностью при изучении JSF является осознание того, что компоненты обладают сохраняемым состоянием. Если вы когда-либо использовали Struts, повторите за мной: ―JSF – это не Struts. JSF – это не Struts‖. Исходя из моих личных наблюдений, разработчики, имеющие опыт создания графических интерфейсов на Swing, AWT, Visual Basic или Delphi, испытывают меньше проблем при освоении JSF, чем те, которые годами использовали Struts и никогда не сталкивались с разработкой GUI. Данное руководство должно помочь вам привыкнуть к мысли о сохраняемости состояния компонент.

JSF предоставляет описываемые тегами компоненты, соответствующие всем полям ввода в стандартном HTML. Кроме этого вы можете создавать специальные компоненты для нужд вашего приложения, а также комбинировать несколько компонент HTML в один сложный объект , например, элемент Data Picker, состоящий из трех выпадающих меню. Все компоненты могут сохранять свое состояние – это поддерживается на уровне самой технологии JSF. Кроме этого компоненты используются для динамической генерации HTML-страниц. Вдобавок к стандартным JSF-компонентам доступно множество других, предоставляемых сторонними разработчиками.

JSF включает в себя:

Модель публикации событий

Простой контейнер для зависимых компонентов (inversion-of-control (IoC) container)

Компоненты, реализующие практически все стандартные возможности графических интерфейсов, включающие в себя:

o Встраиваемую генерацию интерфейса o Валидацию на стороне сервера

o Конвертирование данных

o Управление переходами между страницами

Являясь компонентной архитектурой, JSF легко расширяется и конфигурируется. Большинство функций JSF, например, навигация или управление объектами JavaBean, могут быть реализованы встраиваемыми компонентами. Подобная свобода встраивания компонент обеспечивает значительную гибкость при разработке Web-интерфейсов, а также позволяет легко использовать JSF совместно с другими компонентными технологиями. Например, можно заменить стандартную поддержку IoC в JSF на Spring, предоставляющую полновесное решение для обращения к объектам JavaBean как через IoC, так и с помощью аспектно-ориентированного программирования (AOP).

JSF и JSP

Интерфейс JSF-приложения состоит из страниц JSP (Java Server Pages), которые содержат компоненты, обеспечивающие функциональность интерфейса. При этом библиотеки тегов JSP используются на JSP-страницах для отрисовки компонентов интерфейса, регистрации обработчиков событий, связывания компонентов с валидаторами и конвертаторами данных и много другого.

JSF не привязана к JSP

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

При этом нельзя сказать, что JSF неразрывно связана с JSP, т.к. теги, используемые на JSP-страницах всего лишь отрисовывают компоненты, обращаясь к ним по имени. Жизненный же цикл компонентов JSF не ограничивается JSPстраницей.

Вы это осознаете, как только впервые измените некие атрибуты на JSP-странице, а обновив ее, обнаружите, что ничего не изменилось. Это объясняется тем, что теги на странице обращаются к текущему состоянию компонента. Если компонент был создан ранее, то теги не изменят его состояние. Вместо этого состояние компонент может модифицироваться контроллером (например, он может сделать текстовое поле недоступным для ввода). После этого новое состояние может быть отображено при следующем вызове представления.

Как правило, интерфейсы JSF-приложений создаются вообще без программирования на Java, и лишь слегка задействовав универсальный язык выражений (JSTL EL). Как уже отмечалось выше, существует множество IDE, облегчающих создание и сборку приложений JSF, а также множество сторонних компонент GUI. Но при этом можно работать без всякой поддержки со стороны каких-либо средств WYSIWYF, несмотря на то, что их использование изначально учитывалось при проектировании JSF. Этим мы и займемся в данной статье.

JSF и MVC

JSF проектировалась с учетом опыта накопленного в течение нескольких лет эволюции программных средств для разработки Web-приложений на Java. Все начиналось c JSP – технологии, обладающей немалыми достоинствами, но, к сожалению, позволяющей слишком легко смешивать код на Java с HTML-содержимым Web-страниц. Следующим шагом было появление так называемой ―Модели 1‖ (Model 1), поощряющей перенос Java-кода в компоненты JavaBeans, к которым затем можно было обращаться на странице с помощью тега <jsp:useBean>. Этот подход неплохо работал в случае простых Web-приложений, но раздражал многих Java-разработчиков своим сходством с C++, например, использованием статических директив include. В результате была предложена архитектура под названием ―Model 2‖. Model 2 — это не что иное, как архитектура MVC, приспособленная под нужды Web-приложений. В соответствии с Model 2, контроллер представляет собой сервлеты (или Actions) а отображение страниц реализуется с помощью JSP-страниц. Примером упрощенной реализации Model 2 может служить Apache Struts — библиотека, использующая Actions в качестве сервлетов для контроллера. При этом логика поведения контроллера отделена от данных приложения, размещенных в объектах ActionForms. Struts в основном вызывал недовольство своей скорее процедурной, чем объектноориентированной моделью, чем заслужил прозвище ‖COBOL для Web‖. Другими примерами реализации Model 2 являются WebWork и Spring MVC, которые хотя и более объектно-ориентированны, но не получили такого широкого распространения, как Struts. При этом никто из них не содержит компонентной модели с сохраняемым состоянием, в отличие от JSF. Кстати, Struts2 был спроектирован на основе WebWork, отбросив таким образом первоначальный код Struts. Другими словами, Struts не нравится даже самим разработчикам Struts.

Но главной проблемой Model 2 является то, что событийная модель носит слишком примитивный характер, являясь по суси сильно урезанной версией MVC. Кроме этого, не поддерживаются сохраняемые компоненты, что перекладывает слишком много работы на плечи разработчиков. Более мощная компонентная и событийная модель значительно облегчила бы взаимодействие на всех уровнях приложения. Кроме того, подобно JSP, большинство реализаций Model 2 эффективно не предотвращают смешивание разметки HTML и специальных тегов для отрисовки интерфейса приложения, которые ведут себя почти как компоненты, за исключением того, что не обладают сохраняемым состоянием. Наконец, некоторые реализации, например классический Struts, делают ошибку, разделяя поведение и состояние приложения, оставляя у многих Java-разработчиков ощущение, будто они программируют на COBOL.

Подробнее о реализации MVC в JSF

ВJSF-варианте MVC, связь между слоями модели и представления осуществляется через управляемые объекты JavaBean. В частности из-за этого, в эти объекты, привязанные к JSF, желательно не помещать ни бизнес логику, ни логику сохранения данных, а делегировать эти функции в слой модели самого приложения. В этом случае, к объектам приложения можно получать доступ через свойства управляемых объектов. Лично я предпочитаю разделять управляемые объекты на две категории: те, что привязаны к JSF и те, что нет. Последние я и буду называть объектами модели.

Вотличие от JSP, реализация слоя представления в JSF представляет собой компонентную модель, обладающую сохраняемым состоянием. Слой состоит из двух частей : корня представления и страниц JSP. Корень – это набор компонентов, в которых хранится общее состояние интерфейса. JSF, подобно Swing и AWT, управляет деревом компонентов в соответствии с паттерном ―Компоновщик‖ (Composite). При этом контейнер, с помощью которого осуществляется управление содержащимися в нем компонентами, сам является компонентом. JSF свзязывает компоненты интерфейса с страницами JSP, позволяет ассоциировать поля ввода со свойствами объектов JavaBean (или скорее даже свойствами свойств), а кнопки — с обработчиками событий и методами, реализующими то или иное действие. На рисунке 1 изображена архитектура демонстрационного приложения (того самого, что мы подробно разберем ниже) с точки зрения парадигмы MVC:

Рисунок 1. Демонстрационное приложение, спроектированное в соответствие с принципами MVC

Если, дочитав до этого момента, вы пребываете в некотором недоумении - не волнуйтесь. Худшее уже позади. Понимание концепции JSF — это больше половины работы по ее реализации, и, как вы скоро увидите, игра стоит свеч. Но хватит теории, пора заняться непосредственно программированием!

Процесс создания приложения

Вцелом, процесс разработки калькулятора будет состоять из следующих шагов:

Объявление описание сервлета Faces в дескрипторе Web-приложения (файле web.xml)

Описание местонахождения файла faces-config.xml внутри web.xml

Создание класса Calculator

Объявление Calculator в файле faces-config.xml в качестве объекта JavaBean

Создание страницы index.jsp

Создание страницы calculator.jsp

Структура каталогов приложения будет выглядеть следующим образом:

---+ src

 

 

+---main

 

 

+---

java

 

+---

webapp

 

+---

pages

 

+---

WEB-INF

 

 

+---lib

 

 

 

Сам исходный код на Java будет размещен в каталоге src//main/java. Файл web.xml, а также конфигурационный файл JSF будут расположены в src/main/WEB-INF. Данный пример был создан с помощью среды Eclipse IDE для разработчиков Java EE (так называемая Eclipse JEE), которая включает в себя мастер для автоматической генерации файлов web/xml и facesconfig.xml с заведомо верным набором элементов. Кроме этого предполагается, что для запуска будет использовать сервер приложений, поддерживающий Java EE 5, т.е. включающий в себя JAR-файлы для JSF и JSTL. В

разделе Ресурсы приведены инструкции о том, как сконфигурировать Tomcat для исполнения JSF приложений, Eclipse – для запуска Tomcat, а также как запускать примеры, используя Maven 2.

Описание и отображение сервлета Faces

Перед использованием Faces-сервлета необходимо объявить его в файле web.xml, как показано в листинге 1:

Листинге 1. Объявление сервлета Faces в web.xml

<servlet>

<servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup>

</servlet>

В этом нет ничего нового, за исключением того, что вместо написания собственного сервлета, мы будем использовать стандартный JSF-сервлет для обработки запросов. Сервлет должен вызываться на каждый запрос со страниц JSP, на которых используется тег <f:view> (в частности, мы будем применять его в Калькуляторе). Для этого необходимо добавить отображение сервлета (servlet mapping), указывающее, что через него должны загружаться только JSPстраницы, использующие JSF. Пример показан в листинге 2:

Листинг 2. Отображение сервлета Faces в web.xml

<servlet-mapping>

<servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern>

</servlet-mapping>

Согласно отображению в web.xml, JSF контейнер будет вызывать сервлет Faces для обработки всех запросов, относительный URI которых начинается с /faces/ или заканчивается на *.jsf. Благодаря этому будет корректно инициализироваться контекст Faces и корневой элемент представления перед показом страниц JSF. Корневой элемент содержит дерево компонентов, а контекст Faces служит для взаимодействия приложения с JSF.

Таким образом, URL для загрузки Калькулятора должен быть либо http://localhost:8080/calculator/pages/calculator.jsf либо http://localhost:8080/calculator/faces/pages/calculator.jsp, но ни в коем случае не http://localhost:8080/calculator/pages/calculator.jsp. В последнем случае JSP вызывается вне контекста JSF, а,

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

Конфигурирование Faces с помощью faces-config.xml

По умолчанию сервлет Faces будет пытаться использовать конфигурационный файл faces-config.xml, расположенный в директории WEB-INF Web-приложения. При этом можно использовать дополнительные конфигурационные файлы. Для этого предусмотрен специальный параметр в web.xml под названием javax.faces.application.CONFIG_FILES , в котором можно перечислить имена файлов, разделенные запятыми. Дополнительные файлы используются практически всегда за исключением простейших приложений. Но поскольку наш пример относится к последним, то назовем наш файл facesconfig.xml и поместим его в /src/main/webapp/WEB-INF.

Создание класса Calculator

Теперь мы создадим класс Calculator в стиле POJO (plain old Java object). Заметим, что сам класс никоим образом не зависит от JSF, но может быть использован в JSF-приложении с помощью механизма привязки (binding) методов и свойств. Наш класс содержит два свойства: firstNumber и secondNumber.

Поскольку целью примера является всего лишь создание вашего первого JSF-приложения, то можно использовать максимально простую модель. В данном случае она представляет собой его один объект, показанный в листинге 3. Ниже мы разделим ее на два класса: контроллер и непосредственно класс модели.

Листинг 3. POJO-классCalculator

public class Calculator {

/** Первый операнд */ private int firstNumber = 0;

/** Результат операции */ private int result = 0;

/** Второй операнд */ private int secondNumber = 0;

/** Сложение операндов */ public void add() {

result = firstNumber + secondNumber;

}

/** Перемножение операндов */ public void multiply() {

result = firstNumber * secondNumber;

}

/** Сброс результата */ public void clear() {

result = 0;

}

/* ----------

свойства -------------

*/

public int getFirstNumber() { return firstNumber;

}

public void setFirstNumber(int firstNumber) { this.firstNumber = firstNumber;

}

public int getResult() { return result;

}

public void setResult(int result) { this.result = result;

}

public int getSecondNumber() { return secondNumber;

}

public void setSecondNumber(int secondNumber) { this.secondNumber = secondNumber;

}

}

Код в листинге 3 настолько прост, что не нуждается в пояснениях. Напомним только, что сам по себе класс Calculatorможет использоваться вне JSF.

Объявление объекта Calculator в файле faces-config.xml

Полностью содержимое файла faces-config.xml приведено в листинге 4. Как видите, немалая часть файла служит лишь для связывания файла с XML схемой Java EE JSF. Для объявления же управляемых объектов JavaBean служит элемент<managed-bean>.

Листинг 4. Файл faces-config.xml, содержащий объявления управляемых объектов JavaBean

<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2">

<managed-bean> <managed-bean-name>calculator</managed-bean-name>

<managed-bean-class>com.arcmind.jsfquickstart.model.Calculator</managed-bean-class> <managed-bean-scope>request</managed-bean-scope>

</managed-bean>

</faces-config>

Объявление управляемого объекта состоит из двух частей: имени объекта — calculator —, задаваемого с помощью элемента <managed-bean-name>, и полного имени класса (элемент <managed-bean-class>). При этом класс управляемого объекта обязан содержать конструктор без параметров.

Кроме вышеперечисленных элементов существует еще один - <managed-bean-scope>, который определяет, где JSF будет искать объект. В данном случае это request. Если объект привязан к представлению (как будет показано ниже) и еще не существует на момент обращения, то JSF создаст его автоматически с помощью API универсального языка выражений EL. Все объекты, помещаемые в request, доступны только в течение обработки одного запроса. Как правило, туда имеет смысл помещать объекты, чье состояние не представляет интереса после окончательного отображения страницы в конце обработки запроса.

Создание страницы index.jsp

Страница index.jsp необходима, чтобы гарантировать, что calculator.jsp будет загружена в контексте JSF и благополучно найдет корневой элемент представления. Код index.jsp приведен в листинге 5:

Листинг 5. Содержимое страницы index.jsp, перенаправляющая на calculator.jsp

<jsp:forward page="/faces/calculator.jsp" />

Как видите, все, что делает эта страница – это перенаправление к calculator.jsp, находящейся в каталоге faces, т.е. внутри контекста JSF. Благодаря этому, calculator.jsp получает доступ к корневому элементу представления.

Создание страницы calculator.jsp

Большая часть файла представляет собой обычный HTML, а точнее XHTML. При этом можно вставлять HTML-содержимое внутрь тегов <f:view>, <h:form> и <h:panelGroup>. То, что якобы невозможно смешивать теги HTML и JSF, является распространенным заблуждением. Как правило, это можно делать, за исключением компонента<h:panelGrid>, который может содержать только другие компоненты в качестве дочерних элементов.

Далее мы продемонстрируем процесс создания этой страницы шаг за шагом, потому как она достаточно сложна.

Объявление библиотек тегов

Написание страницы начинается с объявления библиотек тегов для JSF, как показано в листинге 7:

Листинг 7: Импортирование библиотек тегов в calculator.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

Код в листинге 7 содержит директивы для JSP компилятора, объявляющие, что будут использоваться две библиотеки тегов JSF: html и core. Библиотека html включает в себя теги для работы с формами и другими элементами HTML. Все теги, реализующие логику, валидацию данных, контроллер и т.д., включены в библиотеку core.

Тег <f:view>

После того, как готова разметка страницы в виде HTML, необходимо явно указать, что для работы с элементами интерфейса будет использоваться JSF. Для этого используется тег <f:view>, информирующий JSP-контейнер о том, что компоненты будут управляться JSF.

Без этого JSF не сможет построить дерево компонентов или получить доступ к ранее созданному дереву. Пример использования <f:view> приведет в листинге 8:

Листинг 8. Тег <f:view> в calculator.jsp

<f:view>

<h:form id="calcForm">

...

</h:form>

</f:view>

Объявление <f:view> делается в первой строчке листинга, после чего контейнер знает, что управление осуществляется с помощью JSF.

Дочерние элементы <f:view>: тег <h:form>

Во второй строчке листинга 8 используется тег <h:form>, указывающий JSF, что в этом месте необходима форма HTML. Во время отрисовки страницы JSF находит ранее созданные дочерние компоненты формы и вызывает их методы для генерации соответствующего HTML-кода. Компоненты формы могут располагаться на странице произвольным образом, например, так, как располагаются поля для ввода данных в Калькуляторе (см. листинг 9):

Листинг 9. Поля ввода внутри тега <h:form>

<table>

<tr>

<td><h:outputLabel value="First Number" for="firstNumber" /></td> <td><h:inputText id="firstNumber"

value="#{calculator.firstNumber}" required="true" /></td> <td><h:message for="firstNumber" /></td>

</tr>

<tr>

<td><h:outputLabel value="Second Number" for="secondNumber" /> </td>

<td><h:inputText id="secondNumber" value="#{calculator.secondNumber}" required="true" /></td>

<td><h:message for="secondNumber" /></td> </tr>

</table>

Как и ранее, заметим, что в листинге преобладает HTML. В страницах JSF можно использовать любые элементы HTML – span,div,table – JSF не накладывает ограничений на работу дизайнера. Более того, есть даже программы, позволяющие создавать страницы JSF в Dreamweaver (см. Ресурсы). Если же использовать JSF совместно с Facelets, что стало возможным, начиная с JSF 1.1, то работа дизайнера еще более упростится. Напомним, что технология Facelets войдет в состав JSF 2.0.

Обратите внимание, что в листинге 9 в атрибутах values обоих тегов <h:inputText> используется JSF EL (язык выражений JSF), например, value="#{calculator.firstNumber}". На первый взгляд, эти выражения выглядят схоже с выражениями JSTL EL, однако, код JSF EL связывает поля ввода с соответствующими значениями свойств объектов JavaBean. Эта связь двунаправлена, т.е. если значение свойства firstNumber было 100, то в поле ввода будет выведено 100 при отображении страницы. Если пользователь ввел 200, то 200 будет сохранено в свойстве объекта (не учитывая возможности возникновения ошибок при конвертации и валидации данных – об этом мы поговорим позже).

Кроме полей ввода в форме calcForm еще присутствуют три элемента типа commandButtons, задающие определенные действия (см. листинг 10):

Листинг 10. Кнопки внутри тега <h:form>

<div>

<h:commandButton action="#{calculator.add}" value="Add" /> <h:commandButton action="#{calculator.multiply}" value="Multiply" />

<h:commandButton action="#{calculator.clear}" value="Clear" immediate="true"/> </div>

О таблицах стилей

Внешний вид компонентов JSF определяется соответствующими классами из таблиц стилей. Каждый компонент связывается с встроенными (inline) стилями с помощью атрибутов style, а также содержит атрибут styleClass для задания класса CSS. Элемент <h:panelGrid>, который будет рассматриваться в следующей секции, содержит атрибуты style для

присвоения стилей строкам и столбцам таблицы. Далее мы рассмотрим использование каскадных таблиц стилей (CSS) в JSF.

В листинге 10 показано, как связываются кнопки с методамиadd(), multiply() и clear() объекта calculator. Таким образом, при нажатии на кнопку будет вызван соответствующий метод (опять же, если не возникло ошибок конвертации или валидации).

По умолчанию JSF производит валидацию данных формы перед вызовом метода, например add() или multiply(). Однако этот шаг можно пропустить, установив значение атрибута immediate в true, как сделано в листинге 10 для кнопки Clear. В этом случае JSF вызовет метод немедленно (здесь есть тонкости, но об это позже).

Отображение результата: тег <h:panelGroup>

Результаты операций сложения и умножения выводятся внутри тега <f:view> с помощью элемента <h:panelGroup>. Пример показан в листинге 11:

Листинг 11. Вывод результата

<h:panelGroup rendered="#{calculator.result != 0}"> <h4>Results</h4>

<table>

<tr><td>

First Number ${calculator.firstNumber} </td></tr>

<tr><td>

Second Number ${calculator.secondNumber} </td></tr>

<tr><td>

Result ${calculator.result} </td></tr>

</table>

</h:panelGroup>

Как и ранее, в коде страницы преобладает HTML. Обратите внимание, что можно свободно использовать выражения JSP внутри <h:panelGroup>. Кроме того, аналогично другим компонентам JSF этот тег содержит атрибут rendered, управляющий выводом содержимого. Таким образом, секция отображения результата выводится только если значением выражения calculator.result != 0 is является true. В частности, по этой причине секция не отображается при первоначальной загрузке страницы, а выводится только после выполнения операции (если конечно результат не равен нулю). В этом решении есть определенная проблема: оно выносит логику приложения в слой представления. К тому же необходимо корректно показывать нулевой результат арифметических операций, таких как 0 + 0 или 0*7. Далее мы устраним этот недостаток.

Использование компонента <h:panelGrid>

До этого момента нам приходилось писать много HTML-кода. С одной стороны, используя HTML можно в точности добиться желаемой разметки, но с другой – для Web-приложения это может быть не настолько критично как, например,

для рекламного проспекта. Поэтому можно облегчить создание интерфейса, используя компонент <h:panelGrid>, позволяющий размещать дочерние элементы внутри ячеек таблицы.

В листинге 12 показана обновленная версия страницы для ввода данных, созданная с помощью <h:panelGrid>:

Листинг 12. Использование <h:panelGrid> внутри <h:form>

<h:form id="calcForm"> <h4>Calculator</h4>

<h:panelGrid columns="3"> <%-- First Number--%>

<h:outputLabel value="First Number" for="firstNumber" /> <h:inputText id="firstNumber"

value="#{calculator.firstNumber}" required="true" /> <h:message for="firstNumber" />

<%-- Second Number--%>

<h:outputLabel value="Second Number" for="secondNumber" /> <h:inputText id="secondNumber"

value="#{calculator.secondNumber}" required="true" /> <h:message for="secondNumber" />

</h:panelGrid>

Область для вывода результата также претерпела некоторые изменения, как показано в листинге 13:

Листинг 13. Секция для вывода результата с помощью <h:panelGrid>

<h:panelGroup rendered="#{calculator.result != 0}"> <h4>Results</h4>

<h:panelGrid columns="1">

<h:outputText value="First Number #{calculator.firstNumber}"/> <h:outputText value="Second Number #{calculator.secondNumber}"/> <h:outputText value="Result #{calculator.result}"/>

</h:panelGrid>

</h:panelGroup>

Как видите, нам удалось избавиться практически от трети (около 20 строк) всего кода на странице и к тому же значительно улучшить читаемость. Теперь наше приложение глубже завязано на JSF, что, как правило, нравится программистам, и что крайне не любят Web-дизайнеры. Вообще, для каждого проекта важно найти оптимальный баланс между полным контролем над HTML-содержимым страниц и легкостью поддержки будущего приложения, достигающейся при помощи JSF-компонент.

Тут мы впервые встретимся с некоторыми тонкостями использования JSF. Компонент <h:panelGrid> может содержать только дочерние компоненты, в отличие от <h:form>, <f:view> и <h:panelGroup>, внутрь которых можно также помещать обычные фрагменты HTML. Первый элемент <h:panelGrid> на странице включает в себя три колонки, поэтому добавление более трех дочерних компонентов приведет к появлению новой строки. Второй

элемент<h:panelGrid> включает в себя только одну колонку, поэтому каждый из дочерних компонентов отображается в отдельной строке. При выводе страницы <h:panelGrid> преобразуется в таблицу HTML, так что, с точки зрения пользователя, результат будет практически идентичен тому, что было раньше. Еще раз повторим: дочерними элементами могут быть только компоненты JSF. Обычный HTML, добавленный внутрь <h:panelGrid> будет проигнорирован при отрисовке страницы.

Оформление интерфейса с помощью CSS

Если у вас есть опыт использования HTML и CSS, то вы, наверное, представляете, как улучшить интерфейс первой версии Калькулятора. То же самое можно делать и при использовании <h:panelGrid>. Все что требуется – это импортировать таблицу CSS и можно начинать использовать стили внутри <h:panelGrid>. В данном случае мы добавим окантовку к таблице и будем чередовать белый и серебряный фон строк.

Первым делом надо объявить импортирование таблицы стилей, как показано в листинге 14:

Листинг 14. Импортирование таблицы стилей

<head>

<title>Calculator Application</title> <link rel="stylesheet" type="text/css"

href="<%=request.getContextPath()%>/css/main.css" />

</head>

Сама таблица приведена в листинге 15:

Листинг 15. Таблица стилей CSS

oddRow {

background-color: white;

}

evenRow {

background-color: silver;

}

formGrid {

border: solid #000 3px; width: 400px;

}

resultGrid {

border: solid #000 1px; width: 200px;

}

В листинге 15 показаны определения стилей oddRow и evenRow. Они задают белый фон нечетных строк и серебряный фон четных строк соответственно.

Теперь применим эти стили к <h:panelGrid>, как показано в листинге 16:

Листинг 16. Использование стилей в компоненте <h:panelGrid>

<h:panelGrid columns="3" rowClasses="oddRow, evenRow" styleClass="formGrid">

...

</h:panelGrid>

Чтобы применить стили к строкам таблицы необходимо установить значение атрибута rowClasses в oddRow, evenRow. Стиль, определяющий внешнюю рамку для таблицы, применяется с помощью атрибутаstyleClass(styleClass="formGrid"). В результате <h:panelGrid> должен выглядеть как в листинге 17:

Листинг 17. Использование стилей для показа результата в компоненте <h:panelGrid>

<h:panelGrid columns="1" rowClasses="oddRow, evenRow" styleClass="resultGrid">

...

</h:panelGrid>

А сам интерфейс Калькулятора должен принять вид как на рисунке 6:

Рисунок 6. Интерфейс калькулятора, оформленный с помощью CSS

<panelGrid> предоставляет гораздо больше возможностей по поддержке стилей, чем рассматривается в данной статье. За более полной информацией обратитесь к описанию API библиотеки тегов в разделе Ресурсы.

Приведение в порядок сообщений об ошибках

Если бы все пользователи были своего рода технофилами, то, может, их и устроили бы сообщения об ошибках, в противном же случае они малоинформативны. Но это можно исправить несколькими способами. Начнем с добавления метки, как показано в листинге 18:

Листинг 18. Добавление меток

<h:inputText id="firstNumber" label="First Number" ... />

...

<h:inputText id="secondNumber" label="Second Number" .../>

...

Обратите внимание на значение ―First Number‖ атрибута label в <h:inputText>. Теперь при возникновении ошибки сообщение будет выглядеть как на рисунке 7:

Рисунок 7. Вывод сообщений с помощью меток

Теперь в сообщении используется текст метки вместо имени свойства, что значительно понятнее для пользователя. Но, впрочем, поскольку сообщение в любом случае выводится рядом с полем ввода, то название поля можно и вовсе опустить. К тому же сами сообщения слишком длинные. Их можно укоротить, как показано в листинге 19:

Листинг 19. Вывод кратких сообщений

<h:outputLabel value="First Number" for="firstNumber" /> <h:inputText id="firstNumber" label="First Number"

value="#{calculator.firstNumber}" required="true" />

<h:message for="firstNumber" showSummary="true" showDetail="false"/>

В листинге 19 значения атрибутов showSummary и showDetail компонента <h:message> устанавливаются в

значенияtrue и false соответственно. Теперь при ошибке валидации firstNumber сообщение будет выглядеть как: "First Number: 'aaa' must be a number consisting of one or more digits." - а в случае пустого поля secondNumber будет выведено: "Second Number: Validation Error: Value is required". Впрочем, сообщения можно еще улучшить, как будет показано ниже.

В начало

Переопределение текстов сообщений

В JSF 1.2 были добавлены атрибуты requiredMessage и conversionMessage, так что теперь можно переопределять текст сообщений индивидуально для каждого компонента (см. листинг 20):

Листинг 20. Использование атрибутов requiredMessage и converterMessge для вывода коротких сообщений об ошибках

<%-- First Number--%>

<h:outputLabel value="First Number" for="firstNumber" /> <h:inputText id="firstNumber" label="First Number"

value="#{calculator.firstNumber}" required="true" requiredMessage="required" converterMessage="not a valid number" />

<h:message for="firstNumber" /> <%-- Second Number--%>

<h:outputLabel value="Second Number" for="secondNumber" /> <h:inputText id="secondNumber" label="Second Number"

value="#{calculator.secondNumber}" required="true" requiredMessage="required" converterMessage="not a valid number" />

<h:message for="secondNumber" />

Обратите внимание на значения атрибутов requiredMessage="required" converterMessage="not a valid number"компонентов <h:inputText>. Теперь сообщения об ошибках хорошо вписываются в <h:panelGrid>: они выводятся рядом с полями ввода, так что пользователю понятно, к чему они относятся (см. рисунок 8):

Рисунок 8. Вывод коротких сообщений

К сожалению, у этого подхода есть один недостаток: приходится устанавливать значения атрибутов у каждого поля ввода. В простом примере это несложно, но грозит серьезными трудностями при поддержке крупного приложения. Очевидно, что это приводит к определенной избыточности, т.е. нарушает принцип ―не повторяйся‖ (don’t repeat yourself или сокращенно –

DRY).

Добавление контроллера

Далее мы чуть изменим наше приложение, чтобы класс Calculator оставался в виде POJO и не был привязан к JSF. Для этого мы создадим класс-контроллер, который будет управлять объектами модели. Таким образом, от JSF будет зависеть только контроллер, а не слой модели приложения.

Вэтом разделе будет рассмотрено:

Использование механизма dependency injection в JSF

Работа с объектом facesContext

Добавление объектовFacesMessage

Использование элемента <h:messages>

Связывание компонентов с контроллером

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

Добавление метода divide() в класс Calculator

В листинге 23 приведен метод divide() класса Calculator. Далее мы покажем, как корректно обрабатывать ошибки деления на ноль, выводя сообщения для пользователя с помощью объекта FacesMessage:

Листинг 23. Добавление метода divide() в POJO-класс Calculator

package com.arcmind.jsfquickstart.model;

/**

*Calculator. Simple POJO.

*@author Rick Hightower

*/

public class Calculator {

/** Первый операнд */ private int firstNumber = 0;

/** Результат операции */ private int result = 0;

...

/** Операция деления */ public void divide() {

this.result = this.firstNumber / this.secondNumber;

}

/** Сброс результата */ public void clear () { result = 0;

}

...

}

Создание класса-контроллера

Теперь мы создадим новый класс – CalculatorController, содержащий ссылку на POJO-класс Calculator. CalculatorController также связан с тремя компонентами JSF - другими словами, он зависит от классов, являющихся частью JSF. Кроме этого, он отвечает за обработку исключений путем добавления объектов

типа FacesMessage вFacesContext.

CalculatorController содержит ссылки на следующие компоненты JSF:

resultsPanel – компонент типа UIPanel

firstNumberInput – компонент типа UIInput

secondNumberInput – компонент типа UInput

Описание контроллера в файле faces-config.xml

Далее необходимо добавить описание контроллера в файле faces-config.xml и связать его с объектом calculator через dependency injection (см. листинг 26):

Листинг 26. Модифицированная версия faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2">

<application> <message-bundle>messages</message-bundle>

</application>

<managed-bean> <managed-bean-name>calculatorController</managed-bean-name> <managed-bean-class>

com.arcmind.jsfquickstart.controller.CalculatorController </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property>

<property-name>calculator</property-name> <value>#{calculator}</value>

</managed-property> </managed-bean> <managed-bean>

<managed-bean-name>calculator</managed-bean-name> <managed-bean-class>

com.arcmind.jsfquickstart.model.Calculator </managed-bean-class> <managed-bean-scope>none</managed-bean-scope>

</managed-bean>

</faces-config>

Таким образом, изменения затронули все слои приложения. Пришло время остановиться на них поподробнее.

Механизм dependency injection в JSF

JSF поддерживает dependency injection – механизм, с помощью которого ссылки на одни объекты JavaBean можно сохранять в свойствах других подобных объектов. Поскольку в данном случае объект calculator будет доступен только через внешний объект calculatorController, мы поместим его в область видимости none. Это означает, что объект не будет помещен ни в какую специальную область видимости после создания. Фрагмент файла faces-config.xml, описывающий calculator, приведен в листинге 27:

Листинг 27. Объявление управляемого объекта calculator в области видимости none

<managed-bean> <managed-bean-name>calculator</managed-bean-name> <managed-bean-class>

com.arcmind.jsfquickstart.model.Calculator </managed-bean-class> <managed-bean-scope>none</managed-bean-scope>

</managed-bean>

Соседние файлы в папке лабораторная работа 7 (jsf)