14. 执行作

本章介绍如何使用action-state元素来控制在流中的某个点调用作。 它还演示了如何使用decision-state元素来做出流路由决策。 最后,讨论了从流中可能的各个点调用作的几个示例。spring-doc.cadn.net.cn

14.1. 定义 Action 状态

您可以使用action-state元素,如下所示:spring-doc.cadn.net.cn

<action-state id="moreAnswersNeeded">
	<evaluate expression="interview.moreAnswersNeeded()" />
	<transition on="yes" to="answerQuestions" />
	<transition on="no" to="finish" />
</action-state>

以下示例显示了使用前面的action-state要确定是否需要更多答案来完成访谈:spring-doc.cadn.net.cn

<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-state检查结果以查看它是否与声明的到另一个状态的 transition 匹配。 这意味着,如果配置了多个 action,它们将在有序链中调用,直到一个 action 返回一个 result 事件,该事件与 action-state 的状态转换匹配,而其余的则被忽略。 这是 “责任链” (CoR) 模式的一种形式。spring-doc.cadn.net.cn

作调用的结果通常是从此状态转换的条件。 您还可以在当前的RequestContext作为自定义过渡标准的一部分,允许使用复杂的过渡表达式来推理上下文状态。spring-doc.cadn.net.cn

另请注意,action-state(与任何其他状态一样)可以有更多的 On-entry作,这些作从头到尾作为列表调用。spring-doc.cadn.net.cn

14.2. 定义决策状态

您可以使用decision-state元素作为action-state元素来使用方便的 if-else 语法做出路由决策。 以下示例显示了moreAnswersNeededstate(来自上一节中的示例),现在实现为 decision state 而不是 action-state:spring-doc.cadn.net.cn

<decision-state id="moreAnswersNeeded">
	<if test="interview.moreAnswersNeeded()" then="answerQuestions" else="finish" />
</decision-state>

14.3. Action Outcome 事件映射

作通常调用普通 Java 对象上的方法。 调用 时action-statedecision-state元素中,这些方法返回可用于驱动状态转换的值。 由于转换是由事件触发的,因此必须首先将方法返回值映射到Event对象。 下表描述了常见返回值类型的映射方式Event对象:spring-doc.cadn.net.cn

表 1.作方法将值返回到事件 ID 映射
方法返回类型 映射的事件标识符表达式

java.lang.Stringspring-doc.cadn.net.cn

Stringspring-doc.cadn.net.cn

java.lang.Booleanspring-doc.cadn.net.cn

是(对于true)、否(对于false)spring-doc.cadn.net.cn

java.lang.Enumspring-doc.cadn.net.cn

Enum名字spring-doc.cadn.net.cn

任何其它类型spring-doc.cadn.net.cn

成功spring-doc.cadn.net.cn

以下示例调用返回布尔值的方法:spring-doc.cadn.net.cn

<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 变量。 或者,您也可以实现Action接口或从MultiAction基类。 当您在作代码和 Spring Web Flow API 之间具有自然耦合时,这些选项提供了更强的类型安全性。 以下各节显示了每种方法的示例。spring-doc.cadn.net.cn

14.4.1. 调用 POJO 动作

以下示例显示如何调用 POJO作:spring-doc.cadn.net.cn

<evaluate expression="pojoAction.method(flowRequestContext)" />
public class PojoAction {
	public String method(RequestContext context) {
		...
	}
}

14.4.2. 调用自定义作实现

以下示例显示如何调用自定义作实现:spring-doc.cadn.net.cn

<evaluate expression="customAction" />
public class CustomAction implements Action {
	public Event execute(RequestContext context) {
		...
	}
}

14.4.3. 调用MultiAction实现

以下示例显示了如何调用MultiAction实现:spring-doc.cadn.net.cn

<evaluate expression="multiAction.actionMethod1" />
public class CustomMultiAction extends MultiAction {
	public Event actionMethod1(RequestContext context) {
		...
	}

	public Event actionMethod2(RequestContext context) {
		...
	}

	...
}

14.5.作异常

作通常会调用封装复杂业务逻辑的服务。 这些服务可能会引发作代码应处理的业务异常。spring-doc.cadn.net.cn

14.5.1. 使用 POJO Action 处理业务异常

以下示例调用一个作,该作捕获业务异常,将错误消息添加到上下文,并返回结果事件标识符。 结果被视为 flow 事件,然后调用流可以响应该事件。spring-doc.cadn.net.cn

<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

以下示例在功能上等同于上一节中的示例,但作为MultiAction而不是 POJO作。 这MultiAction要求其作方法为签名Event ${methodName}(RequestContext),提供更强的类型安全性,而 POJO作允许更多自由度。spring-doc.cadn.net.cn

<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.5.3. 使用exception-handler元素

通常,您应该在作中捕获异常并返回驱动标准转换的结果事件。 您还可以添加exception-handlersub-元素添加到任何状态类型中,具有bean属性,该属性引用FlowExecutionExceptionHandler. 这是一个高级选项,如果使用不当,可能会使流程执行处于无效状态。 考虑内置的TransitionExecutingFlowExecutionExceptionHandler作为正确实现的示例。spring-doc.cadn.net.cn

14.6. 其他作示例

本章的其余部分介绍了使用作的其他方法。spring-doc.cadn.net.cn

14.6.1. 使用on-start元素

以下示例显示了一个作,该作会创建一个新的Bookingobject 的 API 调用一个 Service 上的方法:spring-doc.cadn.net.cn

<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要渲染其视图的部分片段,请执行以下作:spring-doc.cadn.net.cn

<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
	<on-entry>
		<render fragments="hotelSearchForm" />
	</on-entry>
</view-state>

14.6.3. 使用on-exit元素

以下示例显示了一个状态退出作,该作释放对正在编辑的记录的锁定:spring-doc.cadn.net.cn

<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作:spring-doc.cadn.net.cn

<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作,该作在呈现视图之前加载要显示的酒店列表:spring-doc.cadn.net.cn

<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作:spring-doc.cadn.net.cn

<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. 每个作的名称将成为作的 result 事件的限定符。spring-doc.cadn.net.cn

<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>

在此示例中,流转换为showResults什么时候thingTwo成功完成。spring-doc.cadn.net.cn

14.6.8. 流式作

有时,作需要将自定义响应流式传输回客户端。 例如,在处理打印事件时呈现 PDF 文档的流。 这可以通过让作流式传输内容,然后记录Response Completestatus 在ExternalContext. 这responseCompleteflag 告诉暂停view-state而不是呈现响应,因为另一个对象已经处理了它。 以下作显示了此类作:spring-doc.cadn.net.cn

<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 事件时,流会调用printBoardingPassAction方法。 该作将呈现 PDF,然后将响应标记为完成。spring-doc.cadn.net.cn

14.6.9. 处理文件上传

另一个常见的任务是使用 Web Flow 与 Spring MVC 的MultipartResolver. 正确设置解析程序(如此处所述)并配置提交 HTML 表单后enctype="multipart/form-data",您可以在 transition作中处理文件上传。spring-doc.cadn.net.cn

当您将 Web Flow 与 JSF 一起使用时,下一个清单中显示的文件上传示例无关紧要。 有关如何使用 JSF 上载文件的详细信息,请参阅使用 JSF 处理文件上载

考虑以下清单中的表单:spring-doc.cadn.net.cn

<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>

然后考虑用于处理上传的后备对象:spring-doc.cadn.net.cn

package org.springframework.webflow.samples.booking;

import org.springframework.web.multipart.MultipartFile;

public class FileUploadHandler {

	private transient MultipartFile file;

	public void processFile() {
		//Do something with the MultipartFile here
	}

	public void setFile(MultipartFile file) {
		this.file = file;
	}
}

您可以使用过渡作处理上传,如下所示:spring-doc.cadn.net.cn

<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>

MultipartFile绑定到FileUploadHandlerbean 作为正常表单绑定过程的一部分,以便在执行 transition作期间可以对其进行处理。spring-doc.cadn.net.cn