Статьи

JBoss RichFaces с весны

Эта статья покажет вам, как создать приложение RichFaces с помощью Spring. Мы собираемся заменить стандартные управляемые компоненты JSF на Spring-компоненты. Сначала мы будем использовать подход конфигурации на основе XML, а затем перейдем к подходу аннотаций в Spring.

( Примечание . Шаблон проекта для этой статьи можно скачать здесь )

Если вы хотите сначала попробовать RichFaces со стандартными управляемыми компонентами JSF, не проблема. Начните с этой статьи .

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

Наконец, статья о том, как использовать RichFaces в Spring Web Flow , — это то, что я сохраню для будущей статьи.

Что мы собираемся построить?

Я слышу довольно распространенную просьбу о том, как создать мастера в RichFaces. Итак, это именно то, что мы собираемся построить. Заходите в бар и на каждом столе есть экран, через который вы размещаете заказ. Вы нажимаете, чтобы начать заказ, запускается мастер, где вы вводите всю необходимую информацию и размещаете заказ. У нас также будет возможность просматривать все размещенные заказы. Кроме того, мы сможем изменить внешний вид экрана заказа, используя функцию скининга RichFaces.

Примечание. На всех снимках экрана показан пользовательский скин, который вы собираетесь создать позже.

JBoss RichFaces

JBoss RichFaces — это фреймворк, состоящий из трех основных частей: компонентов JSF с поддержкой AJAX (rich), скинов и CDK (Component Development Kit). Компоненты пользовательского интерфейса RichFaces разделены на две библиотеки тегов a4j: и rich :. Обе библиотеки тегов предлагают готовые компоненты JSF с поддержкой AJAX. Возможность скининга позволяет создавать обложки для приложения JSF с использованием стандартных или пользовательских тем оформления (тем). Наконец, CDK — это средство для создания, генерации и тестирования богатых компонентов JSF.

RichFaces не является реализацией JSF. Компоненты RichFaces UI — это просто дополнительные компоненты AJAX JSF, которые работают с любой реализацией JSF (1.1, 1.2, 2.0), любой технологией представления (Facelets, JSP) и интегрированы с компонентами сторонних производителей (MyFaces, Tomahawk, Trinidad и т. Д.).

Установка RichFaces

Установить RichFaces очень просто.

Загрузка
Загрузите последнюю версию RichFaces с http://www.jboss.org/jbossrichfaces/.

Добавьте три JAR-файла RichFaces в каталог WEB-INF / lib вашего приложения:

  • RichFaces-апи-XXXjar,
  • RichFaces-осущ-XXXjar,
  • RichFaces-щ-XXXjar

RichFaces также зависит от следующих библиотек: commons-beanutils.jar, commons-collection.jar, commons-digester.jar, commons-logging.jar

На момент написания этой статьи последняя версия RichFaces — 3.3.0.

Примечание: для использования Hibernate Validation необходимы дополнительные файлы JAR. Файлы JAR включены в проект. 

Фильтр RichFaces
Зарегистрируйте фильтр RichFaces в файле web.xml:

<filter>   <display-name>Ajax4jsf Filter</display-name>   <filter-name>richfaces</filter-name>   <filter-class>org.ajax4jsf.Filter</filter-class> </filter> <filter-mapping>   <filter-name>richfaces</filter-name>   <servlet-name>Faces Servlet</servlet-name>   <dispatcher>REQUEST</dispatcher>   <dispatcher>FORWARD</dispatcher>   <dispatcher>INCLUDE</dispatcher> </filter-mapping>

 Скиннинг

При желании, чтобы использовать один из существующих скинов, добавьте следующее:

<context-param>   <param-name>org.richfaces.SKIN</param-name>   <param-value>laguna</param-value></context-param>

Примечание: новые скины, такие как laguna, glassX и darkX, доступны в отдельных файлах JAR.

Настройка страницы

Если вы используете Facelets, добавьте это на свою страницу:

xmlns:a4j="http://richfaces.org/a4j"xmlns:rich="http://richfaces.org/rich"

На тот случай, если вы все еще застряли с JSP, вы можете добавить это на свои страницы:

<%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%><%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>

Чуть дальше в этой статье я создал готовый к использованию проект Eclipse с уже настроенными RichFaces и Spring. Мы собираемся импортировать проект в Eclipse и затем начать разработку. В «реальном» мире вы, вероятно, использовали бы Maven 2, но, чтобы упростить задачу и сконцентрироваться на RichFaces и Spring, мы собираемся использовать готовый проект.

весна

Чтобы не повторять сказанное о Spring в многочисленных замечательных статьях и других ресурсах, я не буду перечислять каждую функцию Spring. Вы можете найти многочисленные ресурсы в Интернете. Проще говоря, Spring — это среда, которая значительно упрощает разработку корпоративной Java.

Здесь нас интересует Spring bean и внедрение контроля (здесь есть отличная статья ). Spring часто является отличным кандидатом на средний уровень в веб-приложении. В то время как JSF предлагает нам управляемые bean-компоненты и базовое внедрение зависимостей, Spring-внедрение гораздо более мощно. Зачем использовать два контейнера (JSF и Spring) для управления bean-компонентами, когда вы можете использовать только один?

Как я упоминал в начале, я покажу вам два способа настройки bean-компонентов Spring. Один основан на XML, а другой — на основе аннотаций. Даже конфигурация на основе XML значительно сокращает объем XML-кода, который необходимо написать, по сравнению с конфигурацией XML управляемых компонентов JSF.

Установка пружины

Для целей этого примера Spring устанавливается и настраивается следующим образом.
В приложение добавлены следующие JAR-файлы:

  • весна-beans.jar
  • весна-context.jar
  • весна-core.jar
  • весна-web.jar
  • slf4-апи-1.5.5.jar
  • slf4-простой 1.5.5.jar

Регистрация Spring в web.xml

Добавьте следующее в файл web.xml:

<context-param>   <param-name>contextConfigLocation</param-name>   <param-value>/WEB-INF/spring-beans.xml</param-value></context-param><listener>   <listener-class>org.springframework.web.context.ContextLoaderListener   </listener-class></listener><listener>   <listener-class>    org.springframework.web.context.request.RequestContextListener   </listener-class></listener>

Параметр contextConfigLocation указывает файл конфигурации bean-компонентов Spring, в котором будут определены и управляться bean-компоненты Spring. Это выглядит (когда почти пусто) так:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context-2.5.xsd">    <context:annotation-config /></beans>

Мы будем добавлять больше вещей, как мы идем. На данный момент у нас есть только <context: annotation-config>. Этот тег включает поддержку аннотаций Java EE 5, таких как @PostConstruct, @PreDestory и @Resource.

Файл конфигурации JSF

Поскольку мы собираемся использовать бины Spring, мы хотим, чтобы Spring разрешал бины, и, следовательно, необходимо зарегистрировать распознаватель EL в Spring в файле конфигурации JSF:

<application>  <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> </application>

Это все, что вам нужно для использования RichFaces и Spring.

Начало проекта

Я использовал JBoss Tools для создания проекта. Конечно, вы можете использовать любой инструмент по вашему выбору. Ниже я покажу шаги, как импортировать проект как проект JSF в JBoss Tools. Вы также можете импортировать проект как существующий проект Eclipse. Тебе решать. Если вам нужно установить JBoss Tools, вот [ссылка]

1. Загрузите этот проект
2. Импортируйте проект в Eclipse
         1. Файл / Импорт / Другое
         2. Выберите проект JSF
         3. Укажите на файл web.xml и следуйте указаниям мастера шаги.

Прежде чем мы продолжим, давайте удостоверимся, что проект был развернут правильно и что мы можем запустить его. Запустите сервер, щелкнув правой кнопкой мыши Tomcat в представлении «Серверы» и выбрав «Выполнить».

Щелкните правой кнопкой мыши проект и выберите «Запуск от имени» \ «Запускать на сервере». Вы должны получить эту страницу приветствия.

Сборка приложения

Создание модели

Начнем с создания модели для заказа. В реальном приложении этот объект обычно является сущностью — объектом, который сохраняется и извлекается из базы данных.

Класс называется Order и выглядит следующим образом (обратите внимание на пакет):

package bar.model;public class Order {   private String name;   private String email;   private String drink;   private String comments;   public Order(String name, String email, String drink, String comments) {   super();   this.name = name;   this.email = email;   this.drink = drink;   this.comments = comments;   // getter and setters for each property}

Не забудьте сгенерировать геттеры и сеттеры для каждого свойства. В этом объекте нет ничего интересного.

Создание службы заказа

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

Класс OrderService выглядит следующим образом:

package bar.service;import java.util.ArrayList;import java.util.List;import javax.annotation.PostConstruct;import bar.model.Order;public class OrderService {    private List <Order>orders;    public List<Order> getOrders() {        return orders;    }       public void addOrder (String name, String email, String drink, String comments){        Order order = new Order(name, email, drink, comments);        orders.add(order);    }       @PostConstruct    public void create (){        orders = new ArrayList <Order> ();    }}

Несколько вещей, на которые стоит обратить внимание:

  • Этот класс обслуживания будет хранить список всех заказов.
  • Метод addOrder добавляет новый порядок в список.
  • В @PostConstruct аннотаций помечает метод создания () должен быть вызван сразу же после того , как был создан класс OrderService. Чтобы быть более технически правильным, этот метод вызывается после того, как все инъекции были выполнены. Хотя у класса нет зависимостей, этот метод все еще вызывается и является хорошим местом для инициализации любых свойств. В нашем случае мы инициализируем объект списка. 

Регистрация в качестве

bean-компонента Spring Мы также должны зарегистрировать bean-компонент Service в Spring. Мы хотим, чтобы Spring управлял этим компонентом. Помните, что сначала мы будем использовать XML для настройки этого. Регистрация выглядит так:

<bean id="service" class="bar.service.OrderService" scope="session" />

id — это просто идентификатор или имя этого бина.
Мы помещаем этот боб в область видимости сеанса, потому что мы храним список всех ордеров в нем.

Мы использовали аннотацию @PostConstruct для инициализации свойства списка. Это стандартная аннотация Java 5, которая поддерживается Spring.
Примечание. Чтобы аннотации @PostConstruct, @PreDestory и @Resource работали, в конфигурационный файл Spring необходимо добавить следующее:

<context:annotation-config/>

В качестве альтернативы Spring также предоставляет атрибут init-method при регистрации компонента, как показано здесь:

<bean id="orderService" class="bar.service.Service" scope="session"  init-method="create"/>

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

Третий вариант — реализовать интерфейсы InitializingBean и DisposableBean : [ http://static.springframework.org/spring/docs/2.5.x/reference/beans.html ].

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

Просмотреть бобы

Давайте начнем с класса BarBean :

package bar.view;import java.util.List;import bar.service.Service;public class BarBean {  private OrderService orderService;     @SuppressWarnings("unchecked")   public List getOrders () {      return orderService.getOrders();   }   public int getRowCount () {      return orderService.getOrders().size();   }   public Service getOrderService() {      return orderService;   }   public void setOrderService(Service orderService) {      this.orderService = orderService;   }}

Метод getOrders вызывает класс обслуживания и возвращает все заказы.

getRowCount вернет количество строк. Используется не для рендеринга компонента rich: dataTable, если порядок пуст.

Другой вопрос, как мы можем получить экземпляр orderService в этот bean-компонент? Сначала давайте зарегистрируем этот компонент в конфигурационном файле Spring:

<bean id="barBean" class="bar.view.BarBean" scope="request"/>

Нам все еще приходится иметь дело с userService и тем, как он инициализируется внутри компонента. Это когда Spring DI вступает в игру. Мы хотим, чтобы Spring вводил userService в barBean , когда создается barBean . Для этого мы модифицируем конфигурацию так, чтобы она выглядела следующим образом:

<bean id="barBean" class="bar.view.BarBean" scope="request">   <property name="orderService" >      <ref bean="service" />   </property></bean>

service — это имя, под которым мы зарегистрировали класс Service.

Мы готовы взглянуть на класс WizardBean . Этот класс является бэкэндом для мастера.

package bar.view;import java.util.List;import javax.annotation.PostConstruct;import javax.faces.context.FacesContext;import javax.faces.event.ValueChangeEvent;import javax.faces.model.SelectItem;import org.hibernate.validator.Email;import org.hibernate.validator.NotEmpty;import org.hibernate.validator.Pattern;import bar.service.Service;import bar.utils.JSFUtils;public class WizardBean implements java.io.Serializable {    private static final long serialVersionUID = 1L;    @NotEmpty(message = "Name must not be empty")    @Pattern(regex = ".*[^\\s].*", message = "This string contain only spaces")    private String name;    @NotEmpty    @Email(message = "Invalid email format")    private String email;    private String drink;    private String comments;    private String drinkCategorySelected;       private Service orderService;    private String startPage;       private List<SelectItem> drinkCategory;    private List<SelectItem> drinkList;    // init all drinks    private static final String [] CATEGORY = {"Brandy", "Rum", "Tequila", "Whiskey", "Wine", "Beer"};    private static final String [] BRANDY = {"XO", "VSOP", "VS"};    private static final String [] RUM = {"Medium", "Full-bodied", "Aromatic"};    private static final String [] TEQUILA = {"Reposado", "Añejo", "Blanco"};    private static final String [] WHISKEY = {"Malt", "Grain", "Single Malt", };    private static final String [] WINE = {"Red", "White", "Pink"};    private static final String [] BEER = {"Ales", "Lager", "Specialty Beer", };       public void changeDrink(ValueChangeEvent event) {        String newValue = (String) event.getNewValue();               if (newValue.equals("Brandy")) {drinkList = JSFUtils.createList(BRANDY); drink=BRANDY[0];}        else if (newValue.equals("Rum")) {drinkList = JSFUtils.createList(RUM); drink=RUM[0];}        else if (newValue.equals("Tequila")) {drinkList = JSFUtils.createList(TEQUILA);drink=TEQUILA[0];}        else if (newValue.equals("Whiskey")) {drinkList = JSFUtils.createList(WHISKEY);drink=WHISKEY[0];}        else if (newValue.equals("Wine")) {drinkList = JSFUtils.createList(WINE);drink=WINE[0];}        else if (newValue.equals("Beer")) {drinkList = JSFUtils.createList(BEER);drink=BEER[0];}    }       @PostConstruct    public void create() {        drinkCategorySelected = CATEGORY[0];        drinkCategory = JSFUtils.createList(CATEGORY);        drinkList = JSFUtils.createList(BRANDY);        drink = BRANDY[0];    }    public void save() {        orderService.addOrder(name, email, drink, comments);        this.startPage = "/page1.xhtml";        FacesContext.getCurrentInstance().getExternalContext().getRequestMap()                .remove("wizardBean");    }}

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

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

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

В большом разделе, предшествующем комментарию // init ко всем напиткам, описывается, как создается список напитков. Это не самый красивый способ, но он подойдет для этого примера.

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

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

В этом классе, как и в BarBean , у нас есть ссылка на orderService . Нет, где в классе мы создаем экземпляр свойства. Вы, наверное, догадались, но мы собираемся использовать DI Spring для внедрения сервиса в этот bean-компонент. Давайте зарегистрируем этот компонент в файле конфигурации Spring.

<bean id="wizardBean" class="bar.view.WizardBean" scope="request">   <property name="startPage" value="/page1.xhtml"/>   <property name="orderService" >    <ref bean="service" />   </property></bean>

Свойство startPage инициализируется на первой странице мастера.

OrderService внедряется с компонентом обслуживания .

Последний класс — это JSFUtils :

package bar.utils;import java.util.ArrayList;import java.util.List;import javax.faces.model.SelectItem;public class JSFUtils {   public static List <SelectItem> createList (String [] data){      ArrayList <SelectItem>list = new ArrayList <SelectItem>();      for (String item : data){    list.add (new SelectItem (item, item));      }      return list;   }}

Этот единственный метод в этом классе создает список элементов SelectItem . Мы готовы создавать виды.

Создание видов
Давайте начнем с основного вида, в котором есть кнопка для запуска мастера, а также для отображения всех текущих заказов.
start.xhtml:

<h:form>    <!--  launch wizard button -->    <rich:panel header="Welcome to RichBar">        <a4j:commandButton value="Click to start your order"            oncomplete="#{rich:component('wizard')}.show()" />    </rich:panel>    <rich:editor rendered="false" /></h:form><!--  modal panel where the wizard is running --><rich:modalPanel id="wizard" width="550" height="300">    <f:facet name="header">Drink Order</f:facet>    <f:facet name="controls">        <a href="#" onclick="#{rich:component('wizard')}.hide()">X</a>    </f:facet>    <h:panelGroup id="wizardInclude">        <a4j:include viewId="#{wizardBean.startPage}" />    </h:panelGroup></rich:modalPanel><!--  order table --><a4j:outputPanel id="orders">    <rich:dataTable value="#{barBean.orders}" var="order"        rendered="#{barBean.rowCount>0}" rowKeyVar="row">        <rich:column>            <f:facet name="header">Order #</f:facet>            <h:outputText value="#{row+1}" />        </rich:column>        <rich:column>            <f:facet name="header">Name</f:facet>            <h:outputText value="#{order.name}" />        </rich:column>        <rich:column>            <f:facet name="header">Email</f:facet>            <h:outputText value="#{order.email}" />        </rich:column>        <rich:column>            <f:facet name="header">Drink</f:facet>            <h:outputText value="#{order.drink}" />        </rich:column>        <rich:column>            <f:facet name="header">Comments</f:facet>            <h:outputText value="#{order.comments}" escape="false" />        </rich:column>    </rich:dataTable></a4j:outputPanel></body>

Здесь есть три главных вещи.

В самой верхней части страницы мы размещаем кнопку для запуска мастера. a4j: commandButton открывает модальную панель с помощью встроенной JavaScript-функции RichFaces rich: component (‘id’) и вызывает метод show () на модальной панели.

Далее у нас есть актуальная модальная панель. Две грани определяют заголовок модальной панели и элемент управления для закрытия модальной панели. Чтобы закрыть модальную панель, мы используем функцию JavaScript hide () на модальной панели. Далее идет тег a4j: include. Тег работает аналогично интерфейсу Facelets: include, но также позволяет перемещаться по включенному контенту. Это то, что даст нам функциональность мастера. Помните, что # {wizardBean.startPage} инициализируется в /page1.xhtml в конфигурационном файле Spring.

Последняя часть представляет собой элемент управления rich: dataTable, который отображает все текущие заказы. Таблица не отображается, если список пуст. Мы также определили атрибут rowKeyVar, который содержит текущий номер строки. Мы используем его в качестве номера заказа на дисплее.

Следующие четыре страницы являются частью мастера.

page1.xhtml

<h:form xmlns="http://www.w3.org/1999/xhtml"    xmlns:ui="http://java.sun.com/jsf/facelets"    xmlns:h="http://java.sun.com/jsf/html"    xmlns:f="http://java.sun.com/jsf/core"    xmlns:rich="http://richfaces.org/rich"    xmlns:a4j="http://richfaces.org/a4j">    <a4j:keepAlive beanName="wizardBean" />    <rich:panel>        <h:panelGrid>            <h:panelGroup>                <h:outputLabel value="Email:" for="email" />                <h:outputText value=" (We will email you your receipt!)"                    style="FONT-SIZE: small;" />            </h:panelGroup>            <h:panelGroup>                <h:inputText id="email" value="#{wizardBean.email}">                    <rich:ajaxValidator event="onkeyup" />                </h:inputText>                <rich:message for="email" />            </h:panelGroup>            <a4j:commandButton value="Next" action="next" />        </h:panelGrid>    </rich:panel></h:form>

На первой странице мастера мы просим только электронное письмо, чтобы мы могли отправить клиенту квитанцию ​​по электронной почте (как в магазинах Apple). Обратите внимание, что страница использует rich: ajaxValidator для проверки поля электронной почты. rich: ajaxValidator работает против стандартной проверки гибернации, установленной в бине:

@NotEmpty@Email(message = "Invalid email format")private String email;

Это избавляет от необходимости регистрировать валидатор с компонентом на странице. Еще одно преимущество — это богатство: ajaxValidator пропускает фазы проверки процессов и обновления модели. Поскольку мы только проверяем поле, мы можем немного увеличить производительность, не выполняя эти этапы. Пропуск этих двух фаз идентичен установке bypassUpdates = «true» ( дополнительную информацию см. В руководстве разработчика RichFaces ). 

Еще одна вещь, которую мы должны охватить, — это тег a4: keepAlive, расположенный вверху страницы. Если вы посмотрите на WizardBeanПри регистрации в конфигурационном файле Spring вы заметите, что в сферу действия этого компонента входит запрос. Как вы знаете, этот боб — это то, что поддерживает экраны мастера. Теперь при переходе от экрана мастера к экрану мастера значения, введенные на предыдущем экране, каким-то образом запоминаются. Они запоминаются, потому что на последнем экране ( summary.xhtml ) нам показаны все введенные значения. Но компонент находится в области действия запроса, что означает, что для каждого запроса создается новый компонент, и в этом случае старое значение будет потеряно. Итак, как сохраняются значения?

Это именно то, что делает тег a4j: keepAlive . Хранит боб ( wizardBean) с деревом компонентов пользовательского интерфейса. При обратной передаче восстанавливается дерево компонентов пользовательского интерфейса, а также компонент, возвращающий его в область запроса . Это в основном область видимости страницы, пока мы находимся на этой странице, компонент и его свойства будут доступны нам при обратной передаче.

Способ работы a4j: keepAlive заключается в том, что он добавляет компонент в текущий корень представления пользовательского интерфейса. Важно следующее, хотя мы поместили a4j: keepAlive на первом экране мастера ( page1.xhtml ), технически бин будет добавлен в корневой каталог пользовательского интерфейса main.xhtml. Это также причина, по которой нам нужно было только разместить a4j: keepAlive на первой странице, но не на других экранах мастера. Опять же, поместив a4j: keepAlive на первую включенную страницу (page1.xhtml ), мы действительно добавили тег в представление main.xhtml.

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

public void save() {   orderService.addOrder(name, email, drink, comments);     FacesContext.getCurrentInstance().getExternalContext().getRequestMap()    .remove("wizardBean");}

page2.xhtml

<h:form xmlns="http://www.w3.org/1999/xhtml"    xmlns:ui="http://java.sun.com/jsf/facelets"    xmlns:h="http://java.sun.com/jsf/html"    xmlns:f="http://java.sun.com/jsf/core"    xmlns:rich="http://richfaces.org/rich"    xmlns:a4j="http://richfaces.org/a4j">    <rich:panel>        <h:panelGrid>            <h:outputLabel value="Name:" for="name" />            <h:panelGroup>                <h:inputText id="name" value="#{wizardBean.name}" >                    <rich:ajaxValidator event="onkeyup"/>                </h:inputText>                <rich:message for="name"/>            </h:panelGroup>            <h:panelGroup>                <a4j:commandButton value="Previous" action="prev" />                <a4j:commandButton value="Next" action="next" />            </h:panelGroup>        </h:panelGrid>    </rich:panel></h:form>

Вторая страница мастера предложит ввести имя. Мы снова используем rich: ajaxValidator для проверки поля имени с помощью Hibernate Validation. Валидация определяется следующим образом:

@NotEmpty(message = "Name must not be empty")@Pattern(regex = ".*[^\\s].*", message = "This string contain only spaces")private String name;

 

page3.xhtml

<h:form xmlns="http://www.w3.org/1999/xhtml"    xmlns:ui="http://java.sun.com/jsf/facelets"    xmlns:h="http://java.sun.com/jsf/html"    xmlns:f="http://java.sun.com/jsf/core"    xmlns:rich="http://richfaces.org/rich"    xmlns:a4j="http://richfaces.org/a4j">       <rich:panel>        <h:panelGrid>        <h:panelGrid columns="2">            <h:outputLabel value="Category:" for="want" />            <rich:inplaceSelect id="want" value="#{wizardBean.drinkCategorySelected}"            valueChangeListener="#{wizardBean.changeDrink}">                <f:selectItems value="#{wizardBean.drinkCategory}"/>                <a4j:support event="onviewactivated" ajaxSingle="true" reRender="drink"/>            </rich:inplaceSelect>                       <h:outputLabel value="Drink:" for="drink" />            <rich:inplaceSelect id="drink" value="#{wizardBean.drink}" >                <f:selectItems value="#{wizardBean.drinkList}"/>            </rich:inplaceSelect>                   </h:panelGrid>            <h:panelGroup>                <a4j:commandButton value="Previous" action="prev" />                <a4j:commandButton value="Next" action="next" />            </h:panelGroup>        </h:panelGrid>    </rich:panel></h:form>

На третьем экране мастера используются два полнофункциональных компонента : inplaceSelect . Первый позволяет выбрать категорию напитка. После выбора категории напитки, принадлежащие к этой категории, загружаются во второй выбор через AJAX. Слушатель изменения значения wizardBean.changeDrink вызывается и загружает напитки, связанные с выбранной категорией. Используя тег a4j: support, мы прикрепили событие onchange к компоненту пользовательского интерфейса rich: inplaceSelect, который запускается при изменении значения. Когда событие вызывается, отправляется запрос AJAX.  ajaxSingle = «true» означает, что мы хотим обрабатывать только этот компонент пользовательского интерфейса на сервере. Наконец, reRender указывает на второй список, который мы сейчас возвращаем, со списком напитков.

page4.xhtml

<h:form xmlns="http://www.w3.org/1999/xhtml"    xmlns:ui="http://java.sun.com/jsf/facelets"    xmlns:h="http://java.sun.com/jsf/html"    xmlns:f="http://java.sun.com/jsf/core"    xmlns:rich="http://richfaces.org/rich"    xmlns:a4j="http://richfaces.org/a4j">    <rich:panel>        <h:panelGrid>            <h:outputLabel value="Anything else?" for="comments" />            <rich:editor id="comments" autoResize="true"                            value="#{wizardBean.comments}">            </rich:editor>            <h:panelGroup>                <a4j:commandButton value="Previous" action="prev" />                <a4j:commandButton value="Next" action="next" />            </h:panelGroup>        </h:panelGrid>    </rich:panel></h:form>

На этой странице используется один из новых компонентов пользовательского интерфейса, представленных в RichFaces 3.3.0 — многофункциональный редактор. rich: редактор основан на виджете tinyMCE. Rich: редактор может быть настроен разными способами, здесь мы используем очень простую конфигурацию. Вы можете проверить другой пример здесь .

summary.xhtml

<h:form xmlns="http://www.w3.org/1999/xhtml"    xmlns:ui="http://java.sun.com/jsf/facelets"    xmlns:h="http://java.sun.com/jsf/html"    xmlns:f="http://java.sun.com/jsf/core"    xmlns:rich="http://richfaces.org/rich"    xmlns:a4j="http://richfaces.org/a4j">    <rich:panel>        <h:panelGrid columns="2">            <f:facet name="header">Summary</f:facet>            <h:outputText value="Name" />            <h:outputText value="#{wizardBean.name}" />            <h:outputText value="Email:" />            <h:outputText value="#{wizardBean.email}" />            <h:outputText value="Drink ordered:" />            <h:outputText value="#{wizardBean.drink}" />                       <h:outputText value="Comments:" />            <h:outputText value="#{wizardBean.comments}" escape="false"/>        </h:panelGrid>        <h:panelGrid columns="2">            <a4j:commandButton value="Wait.. Go back" action="prev" />            <a4j:commandButton value="Place Order"                        action="#{wizardBean.save}"                        oncomplete="#{rich:component('wizard')}.hide()"                        reRender="orders, wizard"/>        </h:panelGrid>    </rich:panel></h:form>

Последняя страница — это просто сводная страница. Если какие-либо исправления необходимы, можно нажать кнопку назад. Кнопка «Разместить заказ» связана с методом сохранения, и после завершения действия мы закрываем модальную панель и перерисовываем таблицу — чтобы обновить заказы.

Создание навигации

Мы не сможем перемещаться внутри мастера без определения правил навигации. Правила навигации определены в файле конфигурации JSF и выглядят так:

<navigation-rule>  <from-view-id>/page1.xhtml</from-view-id>  <navigation-case>   <from-outcome>next</from-outcome>   <to-view-id>/page2.xhtml</to-view-id>  </navigation-case> </navigation-rule> <navigation-rule>  <from-view-id>/page2.xhtml</from-view-id>  <navigation-case>   <from-outcome>next</from-outcome>   <to-view-id>/page3.xhtml</to-view-id>  </navigation-case>  <navigation-case>   <from-outcome>prev</from-outcome>   <to-view-id>/page1.xhtml</to-view-id>  </navigation-case> </navigation-rule> <navigation-rule>  <from-view-id>/page3.xhtml</from-view-id>  <navigation-case>   <from-outcome>next</from-outcome>   <to-view-id>/page4.xhtml</to-view-id>  </navigation-case>  <navigation-case>   <from-outcome>prev</from-outcome>   <to-view-id>/page2.xhtml</to-view-id>  </navigation-case> </navigation-rule> <navigation-rule>  <from-view-id>/summary.xhtml</from-view-id>  <navigation-case>   <from-outcome>prev</from-outcome>   <to-view-id>/page3.xhtml</to-view-id>  </navigation-case> </navigation-rule> <navigation-rule>  <from-view-id>/page4.xhtml</from-view-id>  <navigation-case>   <from-outcome>next</from-outcome>   <to-view-id>/summary.xhtml</to-view-id>  </navigation-case>  <navigation-case>   <from-outcome>prev</from-outcome>   <to-view-id>/page3.xhtml</to-view-id>  </navigation-case> </navigation-rule>

Создание пользовательских скинов

Если вы откроете файл web.xml, вы увидите следующую запись, которая определяет текущий скин, используемый в приложении:

<context-param>   <param-name>org.richfaces.SKIN</param-name>   <param-value>laguna</param-value></context-param>

Создать индивидуальный скин очень просто, изменив лишь несколько свойств. Skinnability — это просто расширение CSS, которое позволяет изменять внешний вид всего приложения путем изменения простого файла свойств.

Создайте новый файл свойств в корне JavaSource, назовите его laguna-largefont.skin.properties (все скины должны следовать этому соглашению об именах):

baseSkin=lagunageneralSizeFont=20pxheaderSizeFont=20px

Все, что мы делаем, это используем существующий скин laguna и перезаписываем только два свойства. Теперь просто обновите скин в файле web.xml:

<context-param>   <param-name>org.richfaces.SKIN</param-name>   <param-value>laguna-largefont</param-value></context-param>

Сохраните, перезапустите и запустите приложение.
В этот момент сохраните все и запустите приложение.
Узнайте, как изменить скины во время выполнения.

Переход на весенние аннотации

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

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

Сначала откройте WEB-INF / spring-beans.xml и добавьте следующий тег для поддержки конфигурации на основе аннотаций:

<context:component-scan base-package="bar" />

base-package — это пакет, из которого контейнер Spring начнет искать аннотированные классы.

Далее мы должны прокомментировать (или удалить) конфигурацию bean-компонента на основе XML:

<!--<bean id="barBean" class="bar.view.BarBean" scope="request">    <property name="orderService" >        <ref bean="service" />    </property></bean><bean id="wizardBean" class="bar.view.WizardBean" scope="request">    <property name="startPage" value="/page1.xhtml"/>    <property name="orderService" >        <ref bean="service" />    </property></bean><bean id="service" class="bar.service.Service" scope="session" />-->

Давайте начнем с bean- компонента bar.service.Service :

мы собираемся аннотировать его как компонент Spring:

@Service("orderService")@Scope ("session")public class OrderService {...}

@Service в основном является специализацией аннотации @Component и лучше подходит для аннотирования компонентов, которые выполняют функциональность уровня обслуживания.  В аннотации @Scope указана область, в которую будет помещен этот объект. Если бы мы не указали область действия, область действия по умолчанию была бы Singleton, что означает, что в приложении есть только один раз экземпляр этого объекта, который также работал бы в нашем случае.

Далее нам нужно аннотировать классы barBean и wizardBean . Оба имеют одинаковые аннотации. Давайте сначала начнем с BarBean

@Component("barBean")@Scope("request")public class BarBean { ...}@Autowiredprivate OrderService orderService;

@Component регистрирует компонент как компонент с именем, указанным в скобках. И последнее, что мы должны добавить сервисный компонент в этот компонент. Это делается с помощью аннотации @Autowired . Аннотация будет вводить зависимость, основанную на типе ( аннотация @Qualified может использоваться для получения большего контроля в случае, если контекст Spring содержит более одного объекта ожидаемого типа).  

Наконец, WizardBean выглядит примерно так:

@Component("wizardBean")@Scope("request"public class WizardBean {...}@Autowiredprivate OrderService orderService;

Ну, мы сделали. Сохраните и перезагрузите сервер.

Резюме

Я надеюсь, что этот пример дал вам хорошее представление о том, как использовать RichFaces со Spring. Хотя это все еще можно считать «вводным», мы рассмотрели некоторые основные концепции и функции. Если вы только начинаете с RichFaces, я предлагаю сначала попробовать этот пример, который использует стандартные управляемые компоненты JSF, а затем перейти к этому. Дополнительные примеры и советы RichFaces можно найти в моем блоге по адресу http://mkblog.exadel.com/.