在學習每一門新語言時,第一個程序往往是Hello World。這裏我們寫一個非常簡單的flow,使用常用標籤,在深入講解之前有一個感官上的認識
需求說明
假設有如下簡單流程:要求程序啓動,顯式輸入界面,用戶輸入信息後,點擊提交按鈕,後臺查詢數據庫,然後顯式查詢結果界面,中間任何步驟出錯,都重新返回輸入界面,並顯示錯誤信息。流程大體如下。
需求分析
將上述需求分解:流程啓動時,初始化輸入界面的信息,渲染輸入頁面,用戶點擊提交按鈕,後臺驗證輸入信息的格式是否正確,驗證失敗則返回輸入界面,驗證成功則進行下一步查詢數據庫操作,查詢完成後跳轉到結果頁面進行顯示,流程結束。(請忽略如下流程中判斷圖示不標準的錯誤)
運行前的基本配置
首先需要將FlowRegistry,FlowExecutor,FlowHandlerAdapter,FlowHandlerMapping等項配置好。本文的採用了Spring Web Flow 學習 —— 配置 - 001的配置。
Flow文件
如下配置文件講解:
- 流程啓動時指定flowController的performInit()方法,並返回一個ModelMap類型的對象,分配flowScope作用域下的InitMap變量,將返回的對象賦予該變量;
- 渲染/WEB-INF/jsp/flow/view/input.jsp,並將input界面中上傳的參數與searchForm進行綁定,當觸發submit事件時,跳轉到validate狀態
- validate狀態中,執行flowController的performValidate(searchForm)方法,返回success時跳轉到result狀態,返回fail時跳轉到init狀態,重新渲染input.jsp。
- result狀態,渲染result.jsp,渲染前,首先指定flowController的performSearch(searchForm)方法,該方法返回一個modelMap並分配給flashScope範圍內的modelMap變量。在result界面,無論點擊任何按鍵,只要是向flow在此提交請求,都會跳轉到end狀態,
- end狀態, 重定向到spring首頁
至此,流程結束。
<?xml version="1.0" encoding="UTF-8"?>
<!-- 這是web flow 2.4.5的根標籤形式,2.5.0並不一樣 -->
<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="searchForm" class="cn.floyd.pw.flow.SearchForm"/>
<on-start>
<evaluate expression="flowController.performInit()"
result="flowScope.InitMap"
result-type="org.springframework.ui.ModelMap"/>
</on-start>
<view-state id="init" view="flow/view/input" model="searchForm">
<binder>
<binding property="name"/>
<binding property="gender"/>
</binder>
<transition on="submit" to="validate"/>
</view-state>
<action-state id="validate">
<evaluate expression="flowController.performValidate(searchForm)"/>
<transition on="success" to="result"/>
<transition on="fail" to="input"/>
</action-state>
<view-state id="result" view="flow/view/result">
<on-render>
<evaluate expression="flowController.performSearch(searchForm)" result="flashScope.modelMap"/>
</on-render>
<transition to="end"/>
</view-state>
<end-state id="end" view="externalRedirect:http://springframework.org"/>
</flow>
看完上面的描述,想必剛接觸web flow的人是一臉懵逼
解釋幾個概念就好了。
- 狀態:web flow將一個步驟稱作一個狀態(state),有專門渲染view的view-state,也有隻執行操作的action-state
- 變量:web flow是以xml的形式進行編程的,可以在該xml上下文中定義變量,該變量可以在flow上下文以及flow渲染的jsp文件(使用EL表達式調用),調用的方法中(作爲參數傳入)使用。定義變量的方式主要有兩種,一是通過
<var>
標籤顯式定義,二是在<evaluate>
標籤的result
屬性中定義(此時同時完成了分配變量和變量賦值兩個操作) - 變量作用域:規定了變量的作用範圍,常見的有flashScope(當前狀態有效),flowScope(當前flow有效)等
- 事件:從一個狀態到另一個狀態,一般需要進行觸發,而進行觸發的就是事件。事件可能由view觸發,也可能由方法觸發,我們不用真的去定義一個事件對象。當view-state中的view返回的請求中帶有_eventId的參數時,其值會被自動轉換成Event,當action-state中調用的方法返回字符串時,該字符串也會被自動轉換成Event
- 模型綁定:當view需要提交參數時,可以採用模型綁定的形式,web flow會自動將對應參數綁定到我們的model中,並且還可以自定義驗證和轉換規則。
相關其它文件
FlowController
按照順序列出相關文件,<evaluate>
標籤的expression
屬性可以通過Spring EL表達式的形式直接訪問Spring管理的bean的方法或屬性,也可以訪問flow上下文環境中的對象的方法或屬性。其中主要方法講解如下:
performInit()
初始化下拉選中的初始值,返回modelMap對象,該對象被賦值給flow中的InitMap變量performValidate(SearchForm form)
將SearchForm對象傳入,用於驗證輸入的值,這裏假設全都驗證通過,返回的”success”會被映射成EventperformSearch(SearchForm form)
根據傳入form中的參數進行數據查找,這裏假設已經查找完畢,並將數據放入一個ModelMap,賦值給flow中的modelMap變量
該類是Spring管理的一個bean,
@Controller("flowController")
public class FlowController implements Serializable{
private static final long serialVersionUID = -4439633424434338888L;
public ModelMap performInit() {
ModelMap model = new ModelMap();
model.addAttribute("gender", new String[] {"Man", "Woman"});
return model;
}
public String performValidate(SearchForm form) {
// assume we have pass the validation
return "success";
}
public ModelMap performSearch(SearchForm form) {
// assume we have finished the search process, and got the search result
ModelMap model = new ModelMap();
model.addAttribute("name", form.getName());
model.addAttribute("gender", form.getGender());
model.addAttribute("age", 25);
model.addAttribute("profession", "Programmer");
model.addAttribute("hobbies", new String[] {"Basktball", "Football"});
return model;
}
}
input.jsp
輸入界面,將InitMap
中的值通過EL表達式渲染到下拉選中。通過_eventId=submit
的GET參數方式觸發submit事件。${flowExecutionUrl}
是flow中自管理變量,訪問它才能使得流程繼續下去
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!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>Input page</title>
</head>
<body>
<form action="${flowExecutionUrl}&_eventId=submit" method="get">
Name: <input type="text" name="name"/><br/>
gender:
<select name="gender">
<option value="">- Please Select -</option>
<c:forEach items="${InitMap.gender }" var="gender">
<option value="${gender }">${gender }</option>
</c:forEach>
</select>
<input type="submit" value="Submit">
</form>
</body>
</html>
SearchForm
form,與input.jsp中的form參數對應,注意必須要實現序列化接口。
public class SearchForm implements Serializable {
private String name;
private String gender;
... ...
}
result.jsp
將flow中的modelMap
變量渲染到頁面中
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!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>Result Page</title>
</head>
<body>
Name: ${modelMap.name } <br/>
Gender: ${modelMap.gender } <br/>
Age: ${modelMap.age } <br/>
profession: ${modelMap.profession } <br/>
hobbies:
<c:forEach items="${modelMap.hobbies }" var="hobby">
${hobby }  
</c:forEach>
<br/>
</body>
</html>
調試中遇到的坑
- 現象
點擊form的submit按鈕,無法跳轉到下一個state,始終重新渲染input界面;定義一個<a href="${flowExecutionUrl}&_eventId=submit">
標籤,能夠正常跳轉到下一個state - 原因
待查 - 解決方案
待查