14. 执行操作
本章介绍如何使用 element 控制在流中某个点的操作调用。
它还演示如何使用该元素做出流路由决策。
最后,讨论了从流中可能的各个点调用操作的几个示例。action-state
decision-state
14.1. 定义 Action 状态
当您希望调用一个操作,然后根据操作的结果转换到另一个状态时,可以使用该元素,如下所示:action-state
<action-state id="moreAnswersNeeded">
<evaluate expression="interview.moreAnswersNeeded()" />
<transition on="yes" to="answerQuestions" />
<transition on="no" to="finish" />
</action-state>
以下示例显示了一个访谈流程,该流程使用上述内容来确定是否需要更多答案才能完成访谈: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>
调用每个操作后,将检查结果以查看它是否与声明的转换到另一个状态匹配。
这意味着,如果配置了多个 action,它们将在有序链中调用,直到一个 action 返回一个 result 事件,该事件与 action-state 的状态转换匹配,而其余的则被忽略。
这是 “责任链” (CoR) 模式的一种形式。action-state
操作调用的结果通常是从此状态转换的条件。
您还可以测试当前中的其他信息,作为自定义过渡标准的一部分,这些标准允许使用复杂的过渡表达式来推理上下文状态。RequestContext
另请注意,一个 (与任何其他状态一样) 可以有更多的 on-entry 操作,这些操作从头到尾作为列表调用。action-state
14.2. 定义决策状态
您可以使用该元素作为元素的替代项,以使用方便的 if-else 语法做出路由决策。
以下示例显示了状态(来自上一节中的示例),现在作为决策状态而不是操作状态实现:decision-state
action-state
moreAnswersNeeded
<decision-state id="moreAnswersNeeded">
<if test="interview.moreAnswersNeeded()" then="answerQuestions" else="finish" />
</decision-state>
14.3. Action Outcome 事件映射
操作通常调用普通 Java 对象上的方法。
当调用 from 和 elements 时,这些方法返回可用于驱动状态转换的值。
由于转换是由事件触发的,因此必须首先将方法返回值映射到对象。
下表描述了如何将常见返回值类型映射到对象:action-state
decision-state
Event
Event
方法返回类型 | 映射的事件标识符表达式 |
---|---|
|
值 |
|
yes (赞成), no (赞成 |
|
名称 |
任何其它类型 |
成功 |
以下示例调用返回布尔值的方法:
<action-state id="moreAnswersNeeded">
<evaluate expression="interview.moreAnswersNeeded()" />
<transition on="yes" to="answerQuestions" />
<transition on="no" to="finish" />
</action-state>
14.4. 操作实现
虽然将操作代码编写为 POJO 逻辑是最常见的,但还有其他几个操作实现选项。
有时,您需要编写需要访问流上下文的操作代码。
您始终可以调用 POJO 并将其作为 EL 变量传递。
或者,您可以实现接口或从基类扩展。
当您在操作代码和 Spring Web Flow API 之间具有自然耦合时,这些选项提供了更强的类型安全性。
以下各节显示了每种方法的示例。flowRequestContext
Action
MultiAction
14.4.1. 调用 POJO 动作
以下示例显示如何调用 POJO 操作:
<evaluate expression="pojoAction.method(flowRequestContext)" />
public class PojoAction {
public String method(RequestContext context) {
...
}
}
14.4.2. 调用自定义操作实现
以下示例显示如何调用自定义操作实现:
<evaluate expression="customAction" />
public class CustomAction implements Action {
public Event execute(RequestContext context) {
...
}
}
14.4.3. 调用 ImplementationMultiAction
以下示例显示如何调用 implementation:MultiAction
<evaluate expression="multiAction.actionMethod1" />
public class CustomMultiAction extends MultiAction {
public Event actionMethod1(RequestContext context) {
...
}
public Event actionMethod2(RequestContext context) {
...
}
...
}
14.5. 操作异常
操作通常会调用封装复杂业务逻辑的服务。 这些服务可能会引发操作代码应处理的业务异常。
14.5.1. 使用 POJO Action 处理业务异常
以下示例调用一个操作,该操作捕获业务异常,将错误消息添加到上下文,并返回结果事件标识符。 结果被视为 flow 事件,然后调用流可以响应该事件。
<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. 使用MultiAction
以下示例在功能上等同于上一节中的示例,但作为 而不是 POJO 操作实现。
它要求它的 action methods to be to the signature ,提供更强的类型安全性,而 POJO action 允许更多的自由度。MultiAction
MultiAction
Event ${methodName}(RequestContext)
<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. 元素on-start
以下示例显示了一个操作,该操作通过在服务上调用方法创建新对象:Booking
<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. 元素on-entry
下面的示例演示一个状态进入操作,该操作设置特殊变量,该变量会导致 呈现其视图的部分片段:fragments
view-state
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
<on-entry>
<render fragments="hotelSearchForm" />
</on-entry>
</view-state>
14.6.3. 元素on-exit
以下示例显示了一个状态退出操作,该操作释放对正在编辑的记录的锁定:
<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. 元素on-end
以下示例显示了对象锁定行为,该行为等效于上一节中的示例,但使用流 start 和 end 操作:
<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. 元素on-render
以下示例显示了一个 render 操作,该操作在呈现视图之前加载要显示的酒店列表:
<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. 元素on-transition
以下示例显示了一个将 sub-flow 结果事件属性添加到集合的 transition 操作:
<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. 命名操作
以下示例显示了如何在 .
每个操作的名称将成为操作的 result 事件的限定符。action-state
<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>
在此示例中,流程将转换为 Completed When Successfully。showResults
thingTwo
14.6.8. 流式操作
有时,操作需要将自定义响应流式传输回客户端。
例如,在处理打印事件时呈现 PDF 文档的流。
这可以通过让操作流式传输内容,然后在 上记录状态来实现。
该标志告诉暂停不渲染响应,因为另一个对象已经处理了它。
以下操作显示了此类操作:Response Complete
ExternalContext
responseComplete
view-state
<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();
}
}
在此示例中,当引发 print 事件时,流将调用该方法。
该操作将呈现 PDF,然后将响应标记为完成。printBoardingPassAction
14.6.9. 处理文件上传
另一个常见任务是使用 Web Flow 与 Spring MVC 的 .
正确设置解析程序(如此处所述)并配置了提交 HTML 表单后,您可以在过渡操作中处理文件上传。MultipartResolver
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>
作为正常表单绑定过程的一部分绑定到 Bean,以便在执行 transition 操作期间可以对其进行处理。MultipartFile
FileUploadHandler