14. 执行行动
本章将向您展示如何使用作用态元素用于控制流程中某一点动作的调用。
它还展示了如何使用决策状态元素来做出流路由决策。
最后,讨论了从流程中可能的各个点调用动作的几个例子。
14.1. 行动状态的定义
你可以使用作用态当你希望调用某个动作并根据该动作的结果切换到另一个状态时,元素如下:
<action-state id="moreAnswersNeeded">
<evaluate expression="interview.moreAnswersNeeded()" />
<transition on="yes" to="answerQuestions" />
<transition on="no" to="finish" />
</action-state>
以下示例展示了使用上述方式的面试流程作用态为了确定是否需要更多回答以完成面试:
<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
https://www.springframework.org/schema/webflow/spring-webflow.xsd">
<on-start>
<evaluate expression="interviewFactory.createInterview()" result="flowScope.interview" />
</on-start>
<view-state id="answerQuestions" model="questionSet">
<on-entry>
<evaluate expression="interview.getNextQuestionSet()" result="viewScope.questionSet" />
</on-entry>
<transition on="submitAnswers" to="moreAnswersNeeded">
<evaluate expression="interview.recordAnswers(questionSet)" />
</transition>
</view-state>
<action-state id="moreAnswersNeeded">
<evaluate expression="interview.moreAnswersNeeded()" />
<transition on="yes" to="answerQuestions" />
<transition on="no" to="finish" />
</action-state>
<end-state id="finish" />
</flow>
在调用每个动作后,作用态检查结果是否匹配已声明的转移到另一个状态。
这意味着,如果配置了多个动作,它们会以有序链条调用,直到有一个结果事件与动作状态的状态转换相符,其余事件被忽略。
这是一种“责任链”(CoR)模式。
动作调用的结果通常是退出该状态的标准。
你也可以在当前中测试更多信息请求上下文作为自定义过渡准则的一部分,允许基于上下文状态进行复杂的过渡表达。
还要注意作用态(与其他状态一样)可以有更多从头到尾调用的入场时动作,这些动作以列表形式调用。
14.2. 决策州的定义
你可以使用决策状态作为作用态元素通过方便的 if-else 语法来做出路由决策。
以下示例展示了更多AnswersNeeded。状态(来自前一节示例),现以决策状态实现,而非动作状态:
<decision-state id="moreAnswersNeeded">
<if test="interview.moreAnswersNeeded()" then="answerQuestions" else="finish" />
</decision-state>
14.3. 动作结果事件映射
动作通常会调用普通 Java 对象的方法。
当被召唤时作用态和决策状态元素,这些方法返回值可用于驱动状态转换。
由于转移是由事件触发的,方法返回值必须先映射到事件对象。
下表描述了常见收益值类型如何映射到事件对象:
| 方法返回类型 | 映射事件标识符表达式 |
|---|---|
|
这 |
|
是的(对于 |
|
这 |
任何其他类型 |
成功 |
以下示例调用了返回布尔值的方法:
<action-state id="moreAnswersNeeded">
<evaluate expression="interview.moreAnswersNeeded()" />
<transition on="yes" to="answerQuestions" />
<transition on="no" to="finish" />
</action-state>
14.4. 动作实现
虽然以 POJO 逻辑编写动作代码是最常见的,但还有其他几种动作实现选项。
有时,你需要编写需要访问流程上下文的动作代码。
你总可以调用POJO并传递给flowRequestContext作为一个EL变量。
或者,你也可以实现行动接口或从多重行动基础类。
当你的动作代码与 Spring Web Flow API 之间自然耦合时,这些选项提供了更强的类型安全。
以下章节展示了每种方法的示例。
14.4.1. 启动POJO动作
以下示例展示了如何调用 POJO 动作:
<evaluate expression="pojoAction.method(flowRequestContext)" />
public class PojoAction {
public String method(RequestContext context) {
...
}
}
14.5. 行动例外
动作通常调用包含复杂业务逻辑的服务。 这些服务可以抛出业务异常,动作代码应当处理这些异常。
14.5.1. 处理业务异常时使用POJO动作
以下示例调用一个动作,捕获业务异常,向上下文添加错误消息,并返回结果事件标识符。 结果被视为一个流事件,调用的流可以对此进行响应。
<evaluate expression="bookingAction.makeBooking(booking, flowRequestContext)" />
public class BookingAction {
public String makeBooking(Booking booking, RequestContext context) {
try {
BookingConfirmation confirmation = bookingService.make(booking);
context.getFlowScope().put("confirmation", confirmation);
return "success";
} catch (RoomNotAvailableException e) {
context.addMessage(new MessageBuilder().error().
.defaultText("No room is available at this hotel").build());
return "error";
}
}
}
14.5.2. 处理带有多重行动
以下示例在功能上等价于前一节的示例,但实现为多重行动而不是POJO动作。
这多重行动要求其动作方法的签名Event ${methodName}(RequestContext),提供更强的类型安全,而POJO动作则允许更大的自由度。
<evaluate expression="bookingAction.makeBooking" />
public class BookingAction extends MultiAction {
public Event makeBooking(RequestContext context) {
try {
Booking booking = (Booking) context.getFlowScope().get("booking");
BookingConfirmation confirmation = bookingService.make(booking);
context.getFlowScope().put("confirmation", confirmation);
return success();
} catch (RoomNotAvailableException e) {
context.getMessageContext().addMessage(new MessageBuilder().error().
.defaultText("No room is available at this hotel").build());
return error();
}
}
}
14.6. 其他行动示例
本章剩余部分展示了其他使用动作的方法。
14.6.1. 该启动时元素
以下示例展示了创建一个新预订通过调用服务的方法来实现目标:
<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
https://www.springframework.org/schema/webflow/spring-webflow.xsd">
<input name="hotelId" />
<on-start>
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
</on-start>
</flow>
14.6.2. 该进入时元素
以下示例展示了一个状态进入动作,该动作设置特殊值碎片变量导致视图状态为呈现其视角的部分片段:
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
<on-entry>
<render fragments="hotelSearchForm" />
</on-entry>
</view-state>
14.6.3. 该出场元素
以下示例展示了一个状态退出动作,该动作释放正在编辑记录的锁:
<view-state id="editOrder">
<on-entry>
<evaluate expression="orderService.selectForUpdate(orderId, currentUser)"
result="viewScope.order" />
</on-entry>
<transition on="save" to="finish">
<evaluate expression="orderService.update(order, currentUser)" />
</transition>
<on-exit>
<evaluate expression="orderService.releaseLock(order, currentUser)" />
</on-exit>
</view-state>
14.6.4. 该端部元素
以下示例展示了对象锁定行为,与前文示例等效,但使用流开始和结束动作:
<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
https://www.springframework.org/schema/webflow/spring-webflow.xsd">
<input name="orderId" />
<on-start>
<evaluate expression="orderService.selectForUpdate(orderId, currentUser)"
result="flowScope.order" />
</on-start>
<view-state id="editOrder">
<transition on="save" to="finish">
<evaluate expression="orderService.update(order, currentUser)" />
</transition>
</view-state>
<on-end>
<evaluate expression="orderService.releaseLock(order, currentUser)" />
</on-end>
</flow>
14.6.5. 该渲染时元素
以下示例展示了一个渲染动作,加载一个酒店列表,准备在视图渲染前显示:
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" />
</on-render>
<transition on="select" to="reviewHotel">
<set name="flowScope.hotel" value="hotels.selectedRow" />
</transition>
</view-state>
14.6.6. 该过渡时元素
以下示例展示了一个向集合添加子流结果事件属性的过渡动作:
<subflow-state id="addGuest" subflow="createGuest">
<transition on="guestCreated" to="reviewBooking">
<evaluate expression="booking.guestList.add(currentEvent.attributes.newGuest)" />
</transition>
</subfow-state>
14.6.7. 命名动作
以下示例展示了如何在作用态.
每个动作的名称成为该动作结果事件的限定词。
<action-state id="doTwoThings">
<evaluate expression="service.thingOne()">
<attribute name="name" value="thingOne" />
</evaluate>
<evaluate expression="service.thingTwo()">
<attribute name="name" value="thingTwo" />
</evaluate>
<transition on="thingTwo.success" to="showResults" />
</action-state>
在这个例子中,流转变为节目结果什么时候东西二成功完成。
14.6.8. 流式作
有时,动作需要将自定义响应流回客户端。
例如,处理打印事件时渲染PDF文档的流程。
这可以通过让动作流式内容并记录状态来实现回复完成在外部语境.
这回复 完成旗子告诉停顿视图状态而不是因为另一个物体已经处理了它而发出回应。
以下动作展示了这样的动作:
<view-state id="reviewItinerary">
<transition on="print">
<evaluate expression="printBoardingPassAction" />
</transition>
</view-state>
public class PrintBoardingPassAction extends AbstractAction {
public Event doExecute(RequestContext context) {
// stream PDF content here...
// - Access HttpServletResponse by calling context.getExternalContext().getNativeResponse();
// - Mark response complete by calling context.getExternalContext().recordResponseComplete();
return success();
}
}
在这个例子中,当打印事件被触发时,流程调用打印登机证行动方法。
该动作渲染PDF,然后标记回复为完成。
14.6.9. 文件上传处理
另一个常见任务是结合 Web Flow 处理多部分文件上传,配合 Spring MVC 进行MultipartResolver.
一旦解析器正确设置(如此处所述),提交的HTML表单配置为enctype=“multipart/form-data”,你可以用过渡作来处理文件上传。
| 下一个列表中展示的文件上传示例在使用 Web Flow 配合 JSF 时并不适用。 有关如何使用JSF上传文件的详细信息,请参见“使用JSF处理文件上传”。 |
请考虑以下列表中的表格:
<form:form modelAttribute="fileUploadHandler" enctype="multipart/form-data">
Select file: <input type="file" name="file"/>
<input type="submit" name="_eventId_upload" value="Upload" />
</form:form>
然后考虑处理上传的后方对象:
package org.springframework.webflow.samples.booking;
public class FileUploadHandler {
private transient MultipartFile file;
public void processFile() {
//Do something with the MultipartFile here
}
public void setFile(MultipartFile file) {
this.file = file;
}
}
您可以通过以下过渡作处理上传:
<view-state id="uploadFile" model="uploadFileHandler">
<var name="fileUploadHandler" class="org.springframework.webflow.samples.booking.FileUploadHandler" />
<transition on="upload" to="finish" >
<evaluate expression="fileUploadHandler.processFile()"/>
</transition>
<transition on="cancel" to="finish" bind="false"/>
</view-state>
这多部分文件绑定于文件上传处理程序豆子作为正常形态绑定过程的一部分,以便在过渡动作执行时可供处理。