JSF 2 fu: Cоздание мастера JSF

Создайте свой собственный мастер средствами JSF 2 и CDI

В этой статье серии JSF 2 fu вы узнаете, как с помощью комбинации JSF 2 и технологии Contexts and Dependency Injection (CDI) создать свой собственный мастер с использованием Ajax-вызовов. Статья познакомит вас с примерами применения JSF-шаблонов и технологии Ajax и расскажет о практическом использовании таких функций CDI, как инъекции зависимостей (dependency injection) и контекст последовательности взаимодействий (conversation scope).

Дэвид Джири, президент, Clarity Training, Inc.

David GearyАвтор, лектор и консультант Дэвид Джири является президентом компании Clarity Training, Inc., Он обучает разработчиков создавать Web-приложения с использованием JSF и Google Web Toolkit (GWT). Он участвовал в экспертных группах JSTL 1.0 и JSF 1.0/2.0, был соавтором сертификационного экзамена Web Developer Certification Exam компании Sun, а также принимал участие в проектах с открытым кодом, в том числе Apache Struts и Apache Shale. Книга Дэвида Graphic Java Swing стала одной из самых продаваемых книг о Java, а Core JSF (в соавторстве с Кэем Хорстманом) - одна из самых продаваемых книг о JSF. Дэвид регулярно выступает на конференциях и встречах пользовательских групп. Он является регулярным участником конференций NFJS с 2003 года, проводил курсы в университете Java и дважды удостаивался звания JavaOne rock star.



14.10.2011

JSF 2 – одна из многочисленных мощных технологий, реализованных в Java™ Enterprise Edition (Java EE) 6. В число других таких технологий входит технология Contexts and Dependency Injection (CDI), которая в определенной степени стандартизует концепции, разработка которых велась в течение многих лет в других программных инфраструктурах.

В этой статье я расскажу вам, как с помощью JSF 2 и CDI создать мастер для онлайновых опросов. Мы воспользуемся такими свойствами CDI, как инъекции зависимостей (dependency injection), producer-методы и контекст последовательности взаимодействий (conversation scope), и создадим с их помощью мастер, который в дальнейшем можно будет использовать для реализации онлайновых опросов и викторин.

Об этой серии статей

Серия JSF 2 fu является продолжением серии из трех ознакомительных статей и публикуется под тем же названием. Статьи второй серии помогут вам отточить навыки JSF 2 и стать настоящим мастером кунг-фу в вопросах использования инфраструктуры JSF 2. Вторая серия поможет вам глубже изучить возможности JSF 2 и ее экосистемы, а также представит краткий обзор возможностей интеграции с JSF других технологий Java EE, таких как CDI (Contexts and Dependency Injection).

Помимо технологии CDI, в этой статье мы рассмотрим следующие вопросы:

  • Использование фейслет-шаблонов для минимизации кода и увеличения возможностей повторного использования
  • Использование в мастере технологий Ajax для удобства работы пользователей
  • Использование инъекций зависимостей CDI для упрощения кода
  • Реализация producer-методов CDI для органичного встраивания bean-компонентов в виды приложения
  • Использование контекста последовательности взаимодействий CDI для реализации приложений с множественными запросами.

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

Мастер опроса

На рисунке 1 показан пример работающего мастера опроса:

Рисунок 1. Мастер опроса
Quiz wizard

Сначала приложение отображает всего одну ссылку. Щелчок на ссылке активизируем мастер опроса: <h:commandLink value="#{msgs.startWizard}" action="#{wizard.start}"/>. Текст ссылки (Start the wizard) подгружается из файла свойств и представлен выражением msgs.startWizard в значении ссылки. Вопросы интернационализации относятся к самым азам JSF, так что я не буду забивать вам голову всеми деталями. Достаточно сказать, что приложение полностью локализовано, так что все текстовые строки загружаются из файла messages.properties.

Выполнение примеров кода

Примеры, используемые в этой серии статей, основаны на JSF 2, работающем на сервере приложений, таком, как GlassFish или Resin. Детальную инструкцию по установке и запуску примеров на сервере GlassFish вы найдете в статье JSF 2 fu: Компоненты с поддержкой Ajax. Исходный код примера, рассматриваемого в этой статье, можно скачать по ссылке в разделе Загрузки.

Ссылка Start the wizard перенаправляет пользователя на страницу мастера опроса. В свою очередь, мастер предлагает пользователю ответить на ряд вопросов (по одному вопросу в каждом окне – см. рисунок 1). Для управления кнопками мастера я использую несложный Ajax-код и bean-компонент на стороне сервера – подробнее мы рассмотрим этот вопрос в разделе Ajax.

На рисунке 2 показана страница с последним вопросом и итоговая страница с ответами пользователя. Кнопка Finish становится доступной только когда пользователь ответил на последний вопрос. Нажатие на кнопку Finish открывает итоговую страницу со сводкой вопросов и ответов.

Рисунок 2. Итоговая страница
Wizard summary

Теперь, когда мы разобрались, как работает мастер опроса, посмотрим, как его реализовать.


Приложение quiz

Дерево файлов приложения показано на рисунке 3:

Рисунок 3. Файлы приложения
Application directory

Мой мастер вопросов основан на шаблоне JSF 2 (/templates/wizardTemplate.xhtml), который используется мастером видов (/quizWizard/wizard.xhtml).

Помимо шаблона и видов, у меня есть набор фейслетов — хранящихся в директории quizWizard — для каждого из компонентов мастера:

  • Заголовок (/quizWizard/heading.xhtml)
  • Вопрос (/quizWizard/question.xhtml)
  • Радиокнопки для выбора ответа (quizWizard/choices.xhtml)
  • Кнопки Next, Previous и Finish (quizWizard/controls.xhtml)

Фейслет index.xhtml активизирует приложение по ссылке Start the wizard, а фейслет done.xhtml отображает итоговую страницу с вопросами и ответами.

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


Bean-компоненты приложения, отвечающие за загрузку и отображение вопросов

Bean-компонент Question, код которого показан в листинге 1, определяет вопрос, набор вариантов ответов и сам ответ.

Листинг 1. Bean-компонент Question
package com.clarity;

import java.io.Serializable;

public class Question implements Serializable {
  private static final long serialVersionUID = 1284490087332362658L;

  private String question, answer;
  private String[] choices;
  private boolean answered = false; // следующая кнопка становится доступной 
  // в том случае, когда параметр answered имеет значение true
  
  public Question(String question, String[] choices) {
    this.question = question;
    this.choices = choices;
  }

  public void setAnswer(String answer) {
    this.answer = answer;
    answered = true;
  }

  public String getAnswer()    { return answer;   }
  public String getQuestion()  { return question; }
  public String[] getChoices() { return choices;  }
  public boolean isAnswered()  { return answered; }

  public void setAnswered(boolean answered) { this.answered = answered; }  
}

Набор вопросов, предлагаемых пользователю, хранится в классе Questions. Соответствующий bean-компонент приведен в листинге 2:

Листинг 2. Bean-компонент Questions
package com.clarity;

import java.io.Serializable;

import com.corejsf.util.Messages;

public class Questions implements Serializable {
  private static final long serialVersionUID = -7148843668107920897L;

  private String question;
  private Question[] questions = {      
    new Question(
       Messages.getString("com.clarity.messages", "expandQuestion", null),
       new String[] { 
         Messages.getString("com.clarity.messages", "hydrogen", null),
         Messages.getString("com.clarity.messages", "helium", null),
         Messages.getString("com.clarity.messages", "water", null),
         Messages.getString("com.clarity.messages", "asphalt", null)
       }),
       
   new Question(
       Messages.getString("com.clarity.messages", "waterSGQuestion", null),
       new String[] { 
         Messages.getString("com.clarity.messages", "onedotoh", null),
         Messages.getString("com.clarity.messages", "twodotoh", null),
         Messages.getString("com.clarity.messages", "onehundred", null),
         Messages.getString("com.clarity.messages", "onethousand", null)
       }),
       
   new Question(
       Messages.getString("com.clarity.messages", "numThermoLawsQuestion", null),
       new String[] { 
         Messages.getString("com.clarity.messages", "one", null),
         Messages.getString("com.clarity.messages", "three", null),
         Messages.getString("com.clarity.messages", "five", null),
         Messages.getString("com.clarity.messages", "ten", null)
       }),
       
   new Question(
       Messages.getString("com.clarity.messages", "closestSunQuestion", null),
       new String[] { 
         Messages.getString("com.clarity.messages", "venus", null),
         Messages.getString("com.clarity.messages", "mercury", null),
         Messages.getString("com.clarity.messages", "mars", null),
         Messages.getString("com.clarity.messages", "earth", null)
       })         
  };
  
  public int size()                        { return questions.length; }
  public String getQuestion()              { return question; }
  public void setQuestion(String question) { this.question = question; }
  public Question[] getQuestions()         { return questions; }
}

Код, приведенный в листинге 1 и листинге 2, не содержит ничего выдающегося, он просто передает список вопросов, хранящихся на сервере. Единственным интересным моментом является то, что я программным способом с помощью helper-метода вытаскиваю строки из соответствующего пакета ресурсов. Посмотреть, как работает этот метод, вы можете, загрузив примеры кода. Подробное описание метода можно найти в Core JavaServer Faces (см. Ресурсы).

Это все, что я хотел рассказать о bean-компонентах приложения, за исключением компонента Wizard, который отвечает за управление мастером. Весь интересный Java-код приложения находится именно в этом компоненте. Мы подробно рассмотрим bean-компонент Wizard в разделе CDI: инъекции зависимостей и последовательности взаимодействий.

Познакомившись с иерархией файлов приложения и его bean-компонентами, перейдем к разбору реализации вида мастера.


Шаблон и представление

Структуру большинства мастеров в общем виде можно представить так, как показано на рисунке 4:

Рисунок 4. Структура мастера
Quiz wizard buttons

В листинге 3 приведен шаблон, который формирует такую структуру:

Листинг 3. Шаблон мастера (templates/wizardTemplate.xhtml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

  <h:head>
    <title>
      <ui:insert name="windowTitle">
        #{msgs.windowTitle}
      </ui:insert>
    </title>
  </h:head>
  
  <h:body>  
    <h:outputStylesheet library="css" name="styles.css" target="head"/>       
    
    <ui:insert name="heading"/>
          
    <div class="wizardPanel">
    
      <div class="subheading">
        <ui:insert name="subheading"/>
      </div>
      
       <div class="work">
         <ui:insert name="work"/>
       </div>
       
      <div class="controls">
        <ui:insert name="controls"/>
      </div>
      
    </div>      
        
  </h:body>
</html>

Конкретная реализация мастера опросов приведена в листинге 4:

Листинг 4. Фейслет мастера (quizWizard/wizard.xhtml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
     xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    template="/templates/wizardTemplate.xhtml">

  <ui:define name="heading">
    <ui:include src="heading.xhtml"/>
  </ui:define> 
  
  <ui:define name="subheading">
    <ui:include src="question.xhtml"/>
  </ui:define>
  
  <ui:define name="work">
    <ui:include src="choices.xhtml"/>
  </ui:define>
   
  <ui:define name="controls">
    <ui:include src="controls.xhtml"/>
  </ui:define> 

</ui:composition>

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

В листинге 5 показана секция заголовка мастера опросов:

Листинг 5. heading (quizWizard/heading.xhtml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
     xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

      <div class="heading">
        #{msgs.quizTitle}
      </div>

</ui:composition>

В листинге 6 приведен код секции подзаголовка:

Листинг 6. subheading (quizWizard/question.xhtml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.or g/1999/xhtml"
     xmlns:f="http://java.sun.com/jsf/core"
     xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

  <h:panelGrid columns="1" id="question">
    #{wizard.cursor+1}. #{questions[wizard.cursor].question}?
  </h:panelGrid>
    
</ui:composition>

Абстракция представлений

Использование шаблонов позволяет инкапсулировать повторяющиеся функции, используемые в разных видах, и сосредоточиться только на тех элементах, которые меняются при переходе от одного вида к другому. Например, шаблон мастера включает в себя заголовок окна, страницу стилей и посредством элементов <div> определяет общую структуру каждого вида. Подобная инкапсуляция общих элементов значительно упрощает разработку новых видов – достаточно подключить новые элементы страницы.

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

Код заголовка, приведенный в листинге 5, отображает заголовок опроса — в нашем случае это Science Quiz. Код подзаголовка, указанный в листинге 6, показывает вопрос. Параметр wizard.cursor, использующийся в листинге 6, представляет собой курсор (иначе говоря, индекс), который указывает на текущий вопрос. Значения курсора начинаются с нуля, так что #{wizard.cursor+1} отображает номер вопроса, а #{questions[wizard.cursor].question} отображает собственно вопрос.

Теперь, когда мы покончили с вводной частью (разбором серверных bean-компонентов и шаблонов), перейдем к рассмотрению действительно интересных моментов, а именно, разберем, как реализация мастера использует Ajax и CDI. Начнем с Ajax.


Ajax

Все пользовательские операции в мастере опросов генерируют Ajax-вызовы, так что после обработки таких вызовов обновляются только соответствующие секции страницы. Одна из функций, реализованных с помощью Ajax-вызовов, - это управление кнопками мастера. На рисунке 5 показано, как меняется активное состояние кнопок мастера при ответе на первый и второй вопросы:

Рисунок 5. Кнопки мастера вопросов
Quiz wizard buttons

Кликните, чтобы увидеть увеличенное изображение

Рисунок 5. Кнопки мастера вопросов

Quiz wizard buttons

Ajax-код мастера заключен в двух фейслет-файлах. В листинге 7 показан код файла choices.xhtml:

Листинг 7. Выбор ответа
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
     xmlns:f="http://java.sun.com/jsf/core"
     xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
       
  <h:form id="choices">
    <h:panelGrid columns="2">  
        <h:selectOneRadio value="#{questions[wizard.cursor].answer}"
                         layout="pageDirection">
          <f:selectItems value="#{questions[wizard.cursor].choices}"/>
          <f:ajax render=":buttons"/>
        </h:selectOneRadio>
     </h:panelGrid>
  </h:form> 
    
</ui:composition>

Когда пользователь отмечает одну из радиокнопок, JSF генерирует Ajax-обращение к серверу и записывает выбранную кнопку (ответ на вопрос) в свойстве backing-bean. При возращении вызова JSF обновляет управляющие кнопки мастера.

В листинге 8 приведен код файла controls.xhtml:

Листинг 8. Управляющие кнопки
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
     xmlns:f="http://java.sun.com/jsf/core"
     xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

    <h:form id="buttons">
   
        <h:panelGrid columns="4" styleClass="wizardControls">
        <f:ajax render=":question :choices buttons">

            <h:commandButton id="next" 
                        styleClass="wizardButton"
                             value="#{msgs.nextButtonText}" 
                          disabled="#{not wizard.nextButtonEnabled}"/> 
                    actionListener="#{wizard.nextButtonClicked}"/>
      
            <h:commandButton id="previous"
                        styleClass="wizardButton"
                             value="#{msgs.previousButtonText}" 
                          disabled="#{not wizard.previousButtonEnabled}"
                    actionListener="#{wizard.previousButtonClicked}"/>
        </f:ajax>
                                                              
        <h:commandButton id="finish"
                    styleClass="wizardButton"
                         value="#{msgs.finishButtonText}" 
                      disabled="#{not wizard.finishButtonEnabled}"
                        action="#{wizard.end}"/>
                                                                      
        </h:panelGrid>
        
    </h:form>
</ui:composition>

При щелчке по кнопке Next или Previous JSF генерирует Ajax-обращение к серверу. По возвращении вызова JSF обновляет вопрос и радиокнопки выбора, а также меняет управляющие кнопки.

Кнопка Finish не использует Ajax-вызовов, поскольку щелчок на этой кнопке перенаправляет пользователя на итоговую страницу.

Обращаю ваше внимание на то, что код, приведенный в листингах 7 и 8, содержит множество ссылок на bean-компонент wizard. Этот bean-компонент фактически управляет мастером опросов.


CDI: инъекции зависимостей и последовательности взаимодействий

Образно говоря, CDI можно охарактеризовать как «накачанные» JSF-управляемые bean-компоненты. В качестве компонента Java EE 6 CDI во многом представляет собой стандартизацию концепций, которые в течение ряда лет разрабатывались в рамках инфраструктуры Spring (в частности, инъекции зависимостей (dependency injection) и перехватчики (interceptor)). Фактически CDI и Spring 3 обладают множеством сходных функций.

Благодаря свойствам, которые в CDI называются слабое связывание (loose coupling) и строгая типизация (strong typing), вы получаете возможность эффективно разделять задачи и избавляетесь от необходимости писать рутинный Java-код для создания сущностей объектов и управления их жизненным циклом.

С точки зрения JSF одной из наиболее привлекательных функциональностей CDI является контекст последовательности взаимодействий (conversation scope). Впервые эта функциональность была реализована в инфраструктуре Seam. Она представляет собой контекст использования компонента, жизненный цикл которого управляется программным образом. Применение контекста последовательности взаимодействий позволяет разработчикам избежать взаимоисключающего выбора между запросом и сеансом.

Код мастера, использующий функции CDI, заключен в bean-компоненте Wizard (листинг 9):

Листинг 9. Bean-компонент Wizard
package com.clarity;

import java.io.Serializable;

import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.inject.Produces;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;
import javax.inject.Named;

@Named()
@ConversationScoped()
public class Wizard implements Serializable {
  private static final long serialVersionUID = 1L;
  private Questions questions = new Questions();
  private int cursor = 0;
  
  @Inject
  private Conversation conversation;@Produces @Named
  public Question[] getQuestions() {
    return questions.getQuestions();
  }
  
  public void nextButtonClicked(ActionEvent e) {
    incrementCursor();
  }

  public void previousButtonClicked(ActionEvent e) {
    decrementCursor();
  }
    
  public void incrementCursor() { ++cursor; }
  public void decrementCursor() { --cursor; }
  public int  getCursor()       { return cursor; }
  public void resetCursor()     { cursor = 0; }

  public boolean getNextButtonEnabled() {
    return cursor != questions.size() - 1 &&
    (questions.getQuestions())[cursor].isAnswered();
  }
  
  public boolean getPreviousButtonEnabled() {
    return cursor > 0;
  }
  
  public boolean getFinishButtonEnabled() {
    return cursor == questions.size() - 1 &&
    (questions.getQuestions())[cursor].isAnswered();
  }
  
  public String start() {
    conversation.begin();
    return "quizWizard/wizard";
  }
  
  public String end() {
    conversation.end();
    return "/done";
  }
  
  private void setCurrentQuestionUnanswered() {
    Question currentQuestion = (questions.getQuestions())[cursor];
    currentQuestion.setAnswered(false);    
  }
}

Практически весь интересный код мастера опросов заключен в одном фрагменте, который приведен в листинге 9. Для начала, как я уже упоминал ранее, bean-компонент Wizard определяет методы, которые управляют доступностью кнопок мастера. Кроме того, компонент определяет методы, которые JSF вызывает при нажатии пользователем кнопок Next и Previous. В зависимости от того, какая кнопка нажата, эти методы либо открывают страницу со следующим вопросом, либо возвращают пользователя на предыдущую страницу.

Однако вполне очевидно, что самая главная особенность bean-компонента Wizard состоит в использовании CDI. Сначала, как в примерах других статей этой серии, я использую CDI-версию аннотации @Named вместо аннотации @ManagedBean (CDI-реализация аннотации @Named определяется стандартом JSR 330, Dependency Injection for Java). Обе упомянутые аннотации создают bean-компонент со своим контекстом использования (scope), доступ к которому осуществляется средствами JSF EL. Однако возможности CDI-управляемого bean-компонента значительно шире, так что если вы работаете с сервером приложений, совместимым с Java EE 6, я настоятельно советую вам использовать аннотацию @Named вместо @ManagedBean.

При внимательном изучении листинга 6 и листинга 7 вы, безусловно, обратите внимание на то, что я с помощью языка выражений JSF EL обращаюсь к bean-компоненту questions. Далее, как вы помните, в листинге 2 была приведена реализация класса Questions. Однако аннотации @Named вы в листинге 2 не найдете. В обычных условиях отсутствие аннотации вызовет сообщение об ошибке, однако в нашем случае создание bean-компонента questions обеспечивается методом Wizard.getQuestions(). Этот метод объявляется аннотацией @Produces, а это, в свою очередь, означает, что при упоминании соответствующего bean-компонента в выражении JSF EL JSF вызовет метод Wizard.getQuestions() для получения нового экземпляра компонента Questions.

Следующий интересный момент – использование в компоненте Wizard контекста последовательности взаимодействий. Ссылка Start the wizard на приветственной странице мастера опросов привязана к методу start() bean-компонента Wizard. Метод start(), в свою очередь, обращается к методу begin() для создания последовательности взаимодействий пользователя с сервером (conversation). begin() преобразует текущий запрос (который может рассматриваться как последовательность взаимодействий, состоящая из одного шага) в продолжительную последовательность взаимодействий, которая будет активна до тех пор, пока не будет вызван метод end() или не истечет установленное время таймаута. Поскольку я определил Conversation scope для компонента Wizard, его жизненный цикл заканчивается вместе с окончанием последовательности взаимодействий (conversation).

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

Наконец, обратите внимание на использование CDI-инъекций для добавления последовательности взаимодействий в управляемый bean-компонент. Благодаря таким инъекциям я могу начинать и останавливать последовательность взаимодействий программным способом. Инъекция ресурсов позволяет мне сосредоточиться на решении конкретных задач и не тратить время на банальный код для создания этих ресурсов и управления их жизненным циклом.


Заключение

В этой статье я коснулся сразу нескольких тем — реализация мастера с использованием Ajax, использование шаблонов, инъекции зависимостей, контекст последовательности взаимодействий, — и при этом код нашего приложения остался на удивление коротким. Таким образом, использование JSF 2 и CDI позволяет вам с минимальными затратами создавать надежно работающие веб-приложения с гибкими возможностями многократного использования.

До конца лета публикация статей серии JSF 2 fu будет приостановлена, но осенью я вернусь с новыми примерами, и вы сможете продолжить оттачивать свои приемы и технику использования JSF.


Загрузка

ОписаниеИмяРазмер
Исходный код примера, используемого в статье j-jsf2fu-0710-src.zip13 KБ

Ресурсы

Научиться

Получить продукты и технологии

  • JSF: Загрузите JSF 2.0.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=765504
ArticleTitle=JSF 2 fu: Cоздание мастера JSF
publish-date=10142011