Spring Web Flow. Hi John! Часть 2. Создание приложения

января
12
2012
Метки: spring spring web flow

Содержание

Как упоминалось ранее, Spring Web Flow позволяет реализовать автомат переходов. И если в предыдущей статье выполнение программы было линейным, то в этой появятся переходы. Если пользователь введет короткое имя, то приложение оповестит его об этом и предложит ввести более длинный пароль.

Для начала изменим файл index.jsp, который был автоматически создан в директории WEB-INF. В этом файле добавим ссылку на наш flow:


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Hello John - Spring Web Flow 2.x Tutorial | seostella.com</title>
    </head>
    <body>
        <h1>Spring Web Flow 2.x Tutorial - Hello John</h1>
        <a href="<%= request.getContextPath() %>/hellojohn" 
           title="Go!">Go!</a>
    </body>
</html>

Теперь перейдем к непосредственному созданию приложения. Для сохранения имени пользователя создадим класс User, содержащий всего одно свойство name:


package com.seostella.swfhellojohn.domain;

import java.io.Serializable;

/**
 *
 * @author seostella.com
 */
@SuppressWarnings("serial")
public class User implements Serializable {
   private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Ничего специфического в этом классе нет. Единственная редко используемая конструкция - это @SuppressWarnings("serial"). Если Ваш класс реализует интерфейс java.io.Serializeable, то он должен содержать свойство serialVersionUID. Если же этого свойства нет, то Вы получите предостережение на этапе компиляции. Чтобы избавиться от этого предостережения достаточно добавить @SupressWarnings("serial") перед объявлением класса.

Также нам понадобится еще один класс-компонент, который будет обрабатывать действия, прописанные во flow. Назовем его HellojohnFlowActions:


package com.seostella.swfhellojohn.flow;

import com.seostella.swfhellojohn.domain.User;
import org.springframework.stereotype.Component;

/**
 *
 * @author seostella.com
 */
@Component
public class HellojohnFlowActions {
    
    public User createUser(String name) {
        User user = new User(name);
        return user;
    }
    
}

Класс содержит только один метод createUser, который по принимаемому имени возвращает объект класса User. Еще одна особенность - объект этого класса будет автоматически создан из-за того, что перед объявлением класса находится аннотация @Component, а в конфигурационном файле SWFHelloJohn-servlet.xml есть конструкция, благодаря которой производится поиск на компоненты во всех классах пакета com.seostella.swfhellojohn:


<context:component-scan base-package="com.seostella.swfhellojohn" />

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


    <flow:flow-registry id="flowRegistry" 
                        flow-builder-services="flowBuilderServices"
                        base-path="/WEB-INF/flows">
        <flow:flow-location-pattern value="/**/*-flow.xml" />
    </flow:flow-registry>

указывает на то, что flow необходимо создать в поддиректории, которая находится в директории /WEB-INF/flows/.

Рис 2. Структура WEB-INF
Рис 2. Структура WEB-INF

То есть, создаем директорию /WEB-INF/flows/hellojohn/, а в ней файл hellojohn-flow.xml:


<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow 
  http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
      
    <var name="user" 
         class="com.seostella.swfhellojohn.domain.User"/>      

    <subflow-state id="identifyUser" subflow="hellojohn/user">
        <output name="user" value="user"/>
        <transition on="userReady" to="hellojohn" />
    </subflow-state>
  
    <view-state id="hellojohn">
        <transition to="endState" />
    </view-state>
  
    <end-state id="endState" />
</flow>

Из новых элементов - var и subflow-state. var - переменная, которая инициализируется при старте flow и будет доступна во время выполнения flow. class - класс переменной, который должен реализовывать интерфейс java.io.Serializable.

При старте управление передается элементу subflow-state с идентификатором identifyUser. В свою очередь состояние subflow-state означает, что текущий flow передает управление другому flow, путь к которому содержится в атрибуте subflow тега subflow-state. В нашем случае это flow под названием hellojohn/user.

Возможность передачи управления от одного flow другому создана для облегчения написания кода. Когда количество состояний начинает исчисляться десятками или сотнями, намного легче объединить несколько состояний в один flow, тем самым улучшив читаемость кода. Итак, subflow-state содержит 2 атрибута: id (уникальный идентификатор) и subflow (путь к flow, которому передается управление).


<output name="user" value="user"/>

- этой конструкцией мы передаем переменную user в flow hellojohn/user. Если в дочернем flow переменная изменится, то она изменится и в родительском flow.


<transition on="userReady" to="hellojohn" />

- означает, что после выполнения состояния userReady дочернего flow, управление передается состоянию hellojohn. Между тем, userReady является также конечным состоянием hellojohn/user.

Также в этом flow есть одно состояние-представление с идентификатором hellojohn. Как было сказано в первой части статьи, для отображения будет выбран файл с таким же именем, то есть, hellojohn.jspx:


<html xmlns:jsp="http://java.sun.com/JSP/Page"
     xmlns:form="http://www.springframework.org/tags/form">
  <jsp:output omit-xml-declaration="yes"/>  
  <jsp:directive.page contentType="text/html;charset=UTF-8" />  
  
  <head><title>Hello John - Spring Web Flow 2.x Tutorial | seostella.com</title></head>

  <body>
    <h2>Hello ${user.name}!!!</h2>
    <p>Spring Web Flow 2.x Tutorial</p>
  </body>
</html>

В конструкции "Hello ${user.name}!!!" вместо текста ${user.name} подставиться значение свойства name объекта user (то есть, имя, которое введет пользователь).

Переходим к дочернему flow под названием hellojohn/user. Этот flow должен располагаться в поддиректории user по отношению к hellojohn. Создадим директорию /WEB-INF/flows/hellojohn/user и файл user-flow.xml в ней (структуру файлов директории WEB-INF Вы можете просмотреть на Рис.2):


<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow 
  http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

    <var name="user" class="com.seostella.swfhellojohn.domain.User"/>

    <view-state id="welcome">
        <transition on="nameEntered" to="createUser"/>
    </view-state>
  
    <action-state id="createUser">
        <evaluate result="user" expression=
            "hellojohnFlowActions.createUser(requestParameters.name)" />
        <transition to="userReady" />
    </action-state>  
  
    <end-state id="userReady">
        <output name="user" />
    </end-state>
</flow>

В данном flow присутствует одно состояние-представление. Это состояние с идентификатором welcome. Поэтому создадим файл welcome.jspx:


<html xmlns:jsp="http://java.sun.com/JSP/Page"
     xmlns:form="http://www.springframework.org/tags/form">
  <jsp:output omit-xml-declaration="yes"/>  
  <jsp:directive.page contentType="text/html;charset=UTF-8" />  
  
  <head>
      <title>Hello John - Spring Web Flow 2.x Tutorial | seostella.com</title>
  </head>

  <body>
    <h2>Welcome! Please introduce yourself</h2>

    <form:form>
      <input type="hidden" name="_flowExecutionKey" 
             value="${flowExecutionKey}"/> 
             
      <input type="text" name="name"/><br/>
      
      <input type="submit" name="_eventId_nameEntered" 
             value="Submit" />  
    </form:form>
  </body>
</html>

Рис 3. Страница welcome.jspx
Рис 3. Страница welcome.jspx

Вышеописанная страница будет содержать форму с тремя полями: скрытый _flowExecutionKey, поле для ввода текста name и кнопка отправки данных Submit. Поле _flowExecutionKey и, соответственно, ${flowExecutionKey} необходимы для идентификации каждого из flow, которые выполняются для пользователя. Т.е., у каждого пользователя будет свой уникальный _flowExecutionKey.

Поле name - это обычное поле для ввода текста. Более интересная кнопка для отправки формы, имя которой _eventId_nameEntered. Это означает, что после клика на кнопке будет происходить событие nameEntered. Это событие передается обратно нашему flow, который продолжает выполнение цепочки действий. Т.е., происходит переход на состояние createUser как показано ниже:


    <view-state id="welcome">
        <transition on="nameEntered" to="createUser"/>
    </view-state>

Рассмотрим еще одно состояние createUser:


    <action-state id="createUser">
        <evaluate result="user" expression=
            "hellojohnFlowActions.createUser(requestParameters.name)" />
        <transition to="userReady" />
    </action-state>

Это состояние-действие, в котором выполняется метод createUser объекта hellojohnFlowActions. Объект hellojohnFlowActions - это экземпляр класса HellojohnFlowActions, исходники которого приведены ниже. requestParameters.name - текст, введенный пользователем в текстовом поле name.


package com.seostella.swfhellojohn.flow;

import com.seostella.swfhellojohn.domain.User;
import org.springframework.stereotype.Component;

@Component
public class HellojohnFlowActions {
    
    public User createUser(String name) {
        User user = new User(name);
        return user;
    }
    
}

После выполнения метода createUser управление передается состоянию userReady, которое является конечным в flow под названием user. Поэтому, управление передается назад родительскому flow hellojohn:


    ...
    <subflow-state id="identifyUser" subflow="hellojohn/user">
        <output name="user" value="user"/>
        <transition on="userReady" to="hellojohn" />
    </subflow-state>
  
    <view-state id="hellojohn">
        <transition to="endState" />
    </view-state>
    ...

Как легко догадаться - строка сообщает, что при получении состояния userReady необходимо перейти на состояние hellojohn, которое является состоянием-представлением и поэтому пользователю будет отображена страница hellojohn.jspx:


<html xmlns:jsp="http://java.sun.com/JSP/Page"
     xmlns:form="http://www.springframework.org/tags/form">
  <jsp:output omit-xml-declaration="yes"/>  
  <jsp:directive.page contentType="text/html;charset=UTF-8" />  
  
  <head><title>Hello John - Spring Web Flow 2.x Tutorial | seostella.com</title></head>

  <body>
    <h2>Hello ${user.name}!!!</h2>
    <p>Spring Web Flow 2.x Tutorial</p>
  </body>
</html>

На этой странице пользователь увидит приветствие Hello %имя_пользователя%. Например, если Вы введете имя Bob, то Вам отобразится Hello Bob! как показано ниже:

Рис 4. Страница hellojohn.jspx
Рис 4. Страница hellojohn.jspx

Для запуска flow перейдите на следующий адрес:


http://localhost:8080/SWFHelloJohn/hellojohn

Исходники веб-приложения доступны по следующему адресу - Скачать исходники Spring Web Flow HelloJohn

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

< Spring Web Flow. Hi John! Часть 1. Настройка приложения Spring Web Flow. Hi John! Часть 3. Модификация >

Напишите первое сообщение!

Вы должны войти под своим аккаунтом чтобы оставлять комментарии