Spring Web Flow는 개발자에게 재사용가능하고, flow라고 불리는 자족적인 컨트롤러 모듈을 빌드하는것을 허용한다. flow는 애플리케이션 코드의 수행을 비지니스 목표를 완성하기 위해 다루는 사용자 대화를 위한 청사진이다.
풍부한 도메인 특성 언어(domain specific language-DSL)를 사용하여 명시적으로 정의되는 Flow는 페이지 흐름(flow)의 문제 도메인으로 만들어진다. 최근 이 언어의 Java기반과 XML기반의 형태는 제공된다.
이 장은 Spring Web Flow의 핵심 flow정의 언어를 문서화한다. 시스템의 핵심 도메인 생성과 이러한 생성이 외부 XML에서 표현될수 있는 방법을 배울것이다.
flow는 org.springframework.webflow.Flow의 하나의 인스턴스에 의해 정의된다. 이것은 대화의 정의를 나타내는 중심 도메인 결과이다.
flow는 입력되었을때 행위를 수행하는 flow내 단계를 정의하는 각각의 상태인 하나 이상의 상태의 세트로 구성된다. 수행되는 행위는 상태의 타입과 설정의 함수이다. 이벤트라 불리는 상태 수행의 결과는 상태 변화(transition)를 다루기 위한 flow에 의해 사용된다.
flow 상태중 하나는 flow의 시작시점을 정의하는 startState 이다. 선택적으로, flow는 flow의 종료지점을 정의하는 하나 이상의 end상태를 가질수 있다.
org.springframework.webflow.Flow의 프라퍼티는 밑에 간추려놓았다.
Table 2.1. Flow 프라퍼티
| 프라퍼티 명 | 상세설명 | Cardinality | 디폴트 값 |
|---|---|---|---|
| id | flow의 식별자, 대개 애플리케이션의 다른 flow에 대해 유일하다. | 1 | |
| properties | flow에 대한 추가적인 사용자정의 프라퍼티 | 0..* | None |
| states | flow의 단계 | 1..* | |
| startState | flow의 시작지점 | 1 | |
| startActions | flow의 실행이 시작되는 매번 수행되기 위한 action의 목록 | 0..* | Empty |
| globalTransitions | 이 flow의 모든 상태에 의해 공유되는 변화(transition)의 세트 | 0..* | Empty |
| endActions | flow의 실행이 종료되는 매번 수행되기 위한 action의 목록 | 0..* | Empty |
| exceptionHandlers | 예외가 flow의 상태내에서 던져질때 적용되는 핸들러의 정렬화된 세트 | 0..* | Empty |
| inlineFlows | subflow처럼 불리는 내부 flow의 세트. 이러한 flow는 외부 flow에 대해 지역적으로 범위화된다. | 0..* | Empty |
아래는 Flow프라퍼티가 XML이나 Java코드에 직접 설정될수 있는 방법에 대한 높은 수준의 예제이다.
<flow start-state="startingPoint">
<property .../>
<start-actions>
...
</start-actions>
<-- your state definitions go here -->
<global-transitions>
...
</global-transitions>
<end-actions>
...
</end-actions>
<exception-handler .../>
<inline-flow>
...
</inline-flow>
</flow>
state는 flow의 단계를 위한 행위를 정의한다. 모든 Flow state타입을 위한 기본 클래스는 org.springframework.webflow.State이다. 이 추상 클래스는 모든 state타입에 적용가능한 공통 프라퍼티를 정의한다.
State의 중심 하위클래스는 org.springframework.webflow.TransitionableState이다. 이 추상클래스는 이벤트에 응하는 다른 state에 대한 변화(transition)를 수행하는 모든 state타입에 대한 적용가능한 공통 프라퍼티를 정의한다.
Table 2.3. TransitionableState 프라퍼티
| 프라퍼티 명 | 상세설명 | Cardinality | 디폴트 값 |
|---|---|---|---|
| transitions | 이 state외부의 적절한 경로 | 1..* | |
| exitActions | 이 state가 종료되는 매번 수행하기 위한 action의 목록 | 0..* | Empty |
아래는 이러한 공통 TransitionableState 프라퍼티가 XML이나 Java코드에 직접 설정될수 있는 방법에 대한 높은 수준의 예제이다.
<flow start-state="stateId">
<some-state id="stateId">
<property .../>
<entry-actions>
...
</entry-actions>
<transition .../>
<exit-actions>
...
</exit-actions>
<exception-handler ../>
</some-state>
</flow>
transition은 flow를 통해 path를 정의하는 다른 것에 대한 하나의 state로부터 flow를 가진다.
모든 TransitionableStates를 다시 호출하는 것은 flow에서 경로를 다른 state로 각각 정의하는(또는 경로를 같은 state로 재귀적으로 되돌리는) 하나 이상의 변화(transition)의 세트를 가진다. 변화(transition)가능한 state가 입력될때, 행위를 수행한다. 예를 들어, "Display Form" 라고 불리는 변화(transition)가능한 state는 사용자를 위한 폼을 보여줄것이다. event라 불리는 state 수행의 결과는 stata변화(transition)중 하나의 수행을 다루기 위해 사용된다. 예를 들어, 사용자는 "Process Submit" state를 위한 변화(transition)에 대응되는 submit 이벤트 신호를 보내는 폼 submit버튼을 누를것이다.
이 이벤트-기반 변화(transition) 수행 프로세스는 밑에서 그래픽하게 보여진다.

변화(transition) 수행
변화(transition)는 org.springframework.webflow.Transition 인스턴스에 의해 정의된다. 이것의 프라퍼티는 아래에 요약되어있다.
Table 2.4. 변화(Transition) 프라퍼티
| 프라퍼티 명 | 상세설명 | Cardinality | 디폴트 명 |
|---|---|---|---|
| properties | 변화(transition)를 설명하는 추가적인 프라퍼티. | 0..* | None |
| matchingCriteria | 변화(transition)가 이벤트 발생에 일치하는지 판단하는 전략 | 1 | Always matches |
| executionCriteria | 한번 일치한 변화(transition)가 수행을 허용하는지 판단하는 전략 | 1 | Always allowed |
| targetStateResolver | 변화(transition)의 대상 상태(state)를 계산하는 전략 | 1 |
아래는 Transition이 XML이나 Java코드에 직접 설정될수 있는 방법의 높은 레벨의 예제이다.
<transition on="event" to="targetState">
<property .../>
<action ../>
</transition>
Transition transition = new Transition(new StaticTargetStateResolver("targetState"));
transition.setProperty(..., ...);
transition.setMatchingCriteria(new EventIdTransitionCriteria("event"));
transition.setExecutionCriteria(...);
XML예제에서 transition 요소내 action요소를 위한 지원을 보여준다.
전환(transition)은 executionCriteria의 일부처럼 전환(transition) 자체가 수행되기 전에 수행되는 하나또는 그 이상의 액션과 함께 설정된다. 만약 하나또는 그 이상의 액션이 완벽하게 성공하지 않는다면, 전환은 허용되지 않을것이다. 이 액션 전환 기준(action transition criteria)은 이것이 수행되기 전은 제외하고 전환이 일치한후에 임의의 로직을 수행하는 것을 가능하게 한다. 이것은 당신이 이벤트 후(post)-처리 로직을 수행하길 원할때 유용하다. 좋은 예제는 데이터 바인딩과 폼 서브밋 이벤트후 유효성체크 행위를 수행하는 것이다.
개략적으로 살펴본것처럼, 하나또는 그 이상의 전환은 모든 TransitionableState타입에 추가되고 상태레벨(state-level)에 붙여진다. 선택적으로, 전환은 모든 상태에 의해 공유되는 flow-level에 추가될것이다. 여기엔 global transitions라고 불리는 공유 전환이 있다.
이벤트가 transitionable 상태에서 신호를 보낼때, 상태는 먼저 자체 전환중 하나를 시도하여 맞출것이다. 만약 여기에 상태레벨(state level)에서 일치하지 않는다면, 전역전환의 세트는 테스트될것이다. 여전히 일치하지 않는다면, NoMatchingTransitionException를 던질것이다.
전역전환은 flow의 많은 상태가 같은 전환기준에 공유되는 상태에서 유용하다. 예를들면, flow의 각각의 view를 표현하는 탐색메뉴를 생각해보자. 탐색메뉴 이벤트를 처리하기 위한 로직은 모든 view상태가 필요로한다. 이것은 전역전환이 해결하기 위해 디자인된 문제이다.
다음 예제는 flow레벨에서 상속된 전역전환에 더해 상태레벨(state level)에서 정의된 전환을 보여준다.
<flow start-state="state1">
<some-state id="state1">
<transition on="localEvent1" to="state2"/>
</some-state>
<some-state id="state2">
<transition on="localEvent1" to="state1"/>
</some-state>
<global-transitions>
<transition on="globalEvent1" to="state1"/>
<transition on="globalEvent2" to="state2"/>
</global-transitions>
</flow>
이 예제에서 state1는 하나의 전환을 정의하고 global-transitions요소내 정의된 두개의 다른것들을 상속한다. 이 flow내 정의된 다른 상태(state)는 이 전환전역을 상속할것이다.
이 예제는 아래에서 그림으로 보여진다.

전역전환(Global transitions)
Spring Web Flow는 org.springframework.webflow패키지내 모두 포함된 다섯개의 포함된(built-in) 명백한 state타입을 가진다. 이러한 state들은 다음을 포함하는 공통의 컨트롤러 행위를 수행한다.
사용자에게 flow내 참여하도록 허용(ViewState)
비지니스 애플리케이션 코드를 수행(ActionState)
flow경로 결정을 생성(DecisionState)
다른 flow를 하위 flow처럼 생성(SubflowState)
flow 종료(EndState)
EndState를 제외한 각각의 state타입은 전환가능하다(transitionable). 아래는 이 구조를 보여준다.

State 클래스 다이어그램
입력되면, view state는 사용자(또는 다른 외부 클라이언트)에게 flow내 참여하도록 허용한다. 이 참여 프로세스는 다음과 같이 수행된다.
입력된 view state는 이것을 이슈화하기 위해 필요한 데이터를 가지고 이슈에 논리적으로응답하는 org.springframework.webflow.ViewSelection 를 만든다.
flow수행은 이 state에서 '일시 정지(pause)'된다. 그리고 제어는 시스템을 호출하기 위해 반환된다.
호출 시스템은 사용자에게 인터페이스(또는 다른 응답)를 부여하기 위해 반환된 ViewSelection를 사용한다.
몇몇 "생각하는 시간" 후, 사용자는 '일시 정지된(pause)' 지점에서 flow수행을 다시 하기 위한 입력 이벤트를 서브밋한다.
Spring Web Flow는 view선택 프로세스를 완벽하게 제어하고, 다시 수행하며, view state가 사용자 입력 이벤트에 응답하는 방법을 제공한다. Spring Web Flow가 컨트롤러처럼 응답 표현을 책임지지 않는다는 것을 이해하는것이 중요하다. flow는 사용자 입력이 필요할때 논리적인 view선택을 한다. 이것은 flow가 수행되는 환경을 위해 적합한 응답을 이슈화하는 호출 시스템에 달려있다.
org.springframework.webflow.ViewState의 프라퍼티는 아래에서 개요화되어있다.
org.springframework.webflow.ViewSelection의 프라퍼티는 아래에서 개요화되어있다.
Table 2.6. ViewSelection 프라퍼티
| 프라퍼티 명 | 상세설명 | Cardinality | 디폴트 값 |
|---|---|---|---|
| viewName | 응답의 논리적인 확인자(identifier)는 이슈화될것이다. | 1 | |
| modelMap | 응답내 이슈화될 데이터 | 0..* | Empty |
| redirect | 브라우저 리다이렉트(redirect)가 트리거 될지에 대한 요청 | 1 | False |
ViewState 입력된 ViewState가 org.springframework.webflow.ViewSelector일때, ViewSelection을 만드는 책임을 지는 창조적 전략이다. 이것은 viewName가 계산되는 방법, modelMap가 준비되는 방법, 그리고 리다이렉트가 이슈회되는지를 사용자정의하기 위한 플러그인 지점(plugin-point)을 제공한다. This provides a plugin-point for customizing how the viewName is calculated, how the modelMap is prepared, and whether a redirect should be issued.
Two ViewSelector implementations are provided with Spring Web Flow:
Table 2.7. ViewSelector implementations
| Implementation | Description |
|---|---|
| SimpleViewSelector | Returns a ViewSelection with a static viewName and modelMap containing the union of the attributes in flow scope and request scope. Supports setting a requestConversationRedirect flag that triggers a browser redirect to the selected view. The default implementation. |
| RedirectViewSelector | Returns a ViewSelection that triggers a browser redirect to an abitrary external URL. The viewName is the relative URL to redirect to. Attributes added to the modelMap become URL query parameters. Mainly used by end states to redirect after flow completion. |
The class diagram below shows the ViewState and the associated types used to carry out the view selection process:

ViewState class diagram
The following example shows a view-state definition in XML that makes a simple view selection when entered, selecting the searchForm view for display and, on resume, responding to two possible user input events (submit and cancel) in different ways:
<flow start-state="displaySearchForm">
<view-state id="displaySearchForm" view="searchForm">
<transition on="submit" to="processFormSubmission"/>
<transition on="cancel" to="processCancellation"/>
</view-state>
...
</flow>
The following example shows the equivalent view state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
addViewState("displaySearchForm", "searchForm",
new Transition[] {
transition(on("submit"), to("processFormSubmission")),
transition(on("cancel"), to("processFormCancellation"))
}
);
...
}
}
The following example illustrates a view-state definition in XML that makes a simple view selection when entered, redirecting to the yourList view for display.
<flow start-state="displayList">
<view-state id="displayList" view="yourList" redirect="true">
<transition on="add" to="addListItem"/>
</view-state>
...
</flow>
This is example is called a conversation redirect because the ViewSelection made is cached between request as the "current view selection" for the active conversation. The actual redirect sent is a conversation URL that renders the current view selection on a request into the server.
The following example shows the equivalent view state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
addViewState("displayList", new SimpleViewSelector("yourView", true),
transition(on("add"), to("addListItem"))
);
...
}
}
The following example illustrates a view-state definition in XML that encapsulates typical "form state" behavior.
Consider the requirements of typical input forms. Most forms require pre-render or setup logic to execute before the form is displayed. For example, such logic might load the backing form object from the database, install formatters for formatting form field values, and pull in supporting form data needed to populate drop-down menus.
In addition, most forms require post-back or submission logic to execute when the form is submitted. This logic typically involves binding form input to the backing form object and performing type conversion and data validation.
This "form state" behavior of form setup, display, and post-back is handled elegantly in Spring Web Flow by the capabilities of the view-state construct. See below:
<flow start-state="displayForm">
<view-state id="displayForm" view="form">
<entry-actions>
<action bean="formAction" method="setupForm"/>
</entry-actions>
<transition on="submit" to="saveForm">
<action bean="formAction" method="bindAndValidate"/>
</transition>
</view-state>
...
</flow>
This reads "when the displayForm state is entered, execute the setupForm method on the formAction and render the form view. On submit transition to the saveForm state if the bindAndValidate method on the formAction executes successfully."
When entered, an action state executes business application code, then responds to the result of that execution by deciding what state in the flow to enter next. Specifically:
The entered action state executes an ordered list of one or more org.springframework.webflow.Action instances. This Action interface is the central abstraction that encapsulates the execution of a logical unit of application code.
The state determines if the outcome of the first action's execution matches a transition. If there is a match, the transition is executed. If there is no match, the next action in the list is executed. This process continues until a transition is matched or the list of actions is exhausted.
Spring Web Flow gives you full control over implementing your own actions and configuring when they should be invoked within the lifecycle of a flow. The system can also automatically adapt methods on your existing application objects (POJOs) to the Action interface in a non-invasive manner.
The properties of a org.springframework.webflow.ActionState are summarized below:
Table 2.8. ActionState properties
| Property name | Description | Cardinality | Default value |
|---|---|---|---|
| actions | The ordered list of actions to execute when the state is entered. | 1..* |
As outlined, the ActionState is the central state type for invoking one or more actions and responding to their result to drive a state transition. There are also other points within the lifecycle of a flow where a chain of actions can be executed. At all of these points the only requirement is that these actions implement the central org.springframework.webflow.Action interface.
Table 2.9. Points in a Flow where an Action can be executed
| Point | Description |
|---|---|
| on flow start | Each time an execution of a flow starts. |
| on state entry | Each time a state enters. |
| on transition | Each time a state transition is matched but before it is executed. |
| on state exit | Each time a transitionable state exits. |
| on flow end | Each time an execution of a flow terminates. |
An Action may be annotated with properties by wrapping the Action in a decorator, an instance of org.springframework.webflow.AnnotatedAction. These properties may provide descriptive characteristics, or may be used to affect the action's execution in a specific usage context.
Support for setting several common properties are provided for convenience. These include:
Table 2.10. Common Action properties
| Property name | Description |
|---|---|
| caption | A short description about the action, suitable for display as a tooltip. |
| description | A long description about the action, suitable for display in a text box. |
| name | The name of the action, used to qualify the action's result event. For example, an Action named placeOrder that returns success would be assigned a result event identified by placeOrder.success. This allows you to distinguish logical execution outcomes by action, useful when invoking multiple actions as part of a chain. |
| method | The name of the target method on the Action instance to invoke to carry out execution. This facilitates multiple action methods per Action instance. |
| resultName | If the target method is an arbitrary public method that returns a value, this is the name of the attribute the value should be indexed under. |
| resultScope | If the resultName property is specified, this is the scope the result attribute should be indexed in. For example, "request" or "flow" scope. |
The class diagram below shows the ActionState and the associated types used to carry out the action execution process:

ActionState class diagram
The following example constructs an ActionState definition from XML that executes a single action when entered and then responds to its result:
<flow start-state="displaySearchCriteria">
...
<action-state id="executeSearch">
<action bean="searchAction"/>
<transition on="success" to="displayResults"/>
</action-state>
...
</flow>
This state definition reads "when the executeSearch state is entered, execute the searchAction. On successful execution, transition to the displayResults state."
The association between the searchAction id and an Action implementation is made by the XmlFlowBuilder at Flow configuration time using a service registry, typically a Spring BeanFactory. For example:
<beans>
<bean id="searchAction" class="example.webflow.SearchAction"/>
</beans>
... associates the searchAction with a single instance of example.webflow.SearchAction.
A simple SearchAction implementation might look like this:
public class SearchAction implements Action {
private SearchService searchService;
public SearchAction(SearchService searchService) {
this.searchService = searchService;
}
public Event execute(RequestContext context) {
// lookup the search criteria in "flow scope"
SearchCriteria criteria =
(SearchCriteria)context.getFlowScope().getAttribute("criteria");
// execute the search
Collection results = searchService.executeSearch(criteria);
// set the results in "request scope"
context.getRequestScope().setAttribute("results", results);
// return "success"
return new Event(this, "success");
}
}
The following example constructs the equivalent action state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
...
addActionState("executeSearch", action("searchAction"),
transition(on("success"), to("displayResults")));
...
}
}
The next example constructs an ActionState definition from XML that executes a single action method on a org.springframework.webflow.MultiAction and then responds to its result:
<flow start-state="displaySearchCriteria">
...
<action-state id="executeSearch">
<action bean="searchFlowAction" method="executeSearch"/>
<transition on="success" to="displayResults"/>
</action-state>
...
</flow>
This state definition reads "when the executeSearch state is entered, execute the executeSearch method on the searchFlowAction. On successful execution, transition to the displayResults state."
A SearchFlowAction implementation containing multiple action methods might look like this:
public class SearchFlowAction extends MultiAction {
private SearchService searchService;
public SearchAction(SearchService searchService) {
this.searchService = searchService;
}
public Event executeSearch(RequestContext context) {
// lookup the search criteria in "flow scope"
SearchCriteria criteria =
(SearchCriteria)context.getFlowScope().getAttribute("criteria");
// execute the search
Collection results = searchService.executeSearch(criteria);
// set the results in "request scope"
context.getRequestScope().setAttribute("results", results);
// return "success"
return success();
}
public Event someOtherRelatedActionMethod(RequestContext context) {
return success();
}
public Event yetAnotherRelatedActionMethod(RequestContext context) {
return success();
}
}
As you can see, this allows you to define one to many action methods per Action class. With this approach, there are two requirements:
Your Action class must extend from org.springframework.webflow.MultiAction, or another class that extends from MultiAction. The multi action cares for the action method dispatch that is based on the value of the method property.
Each action method must conform to the signature illustrated above: public Event ${method}(RequestContext) { ... }
The following example constructs the equivalent action state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
...
addActionState("executeSearch", method("executeSearch", action("searchAction")),
transition(on("success"), to("displayResults")));
...
}
}
The next example constructs a ActionState definition from XML that executes a single method on your Plain Old Java Object (POJO) and then responds to the result:
<flow start-state="displaySearchCriteria">
...
<action-state id="executeSearch">
<action bean="searchService" method="executeSearch(${flowScope.criteria})"
resultName="results"/>
<transition on="success" to="displayResults"/>
</action-state>
...
</flow>
This state definition reads "when the executeSearch state is entered, execute the executeSearch method on the searchService passing it the object indexed by name criteria in flowScope. On successful execution, expose the method return value in request scope under the name results and transition to the displayResults state."
In this example, the referenced bean searchService would be your application object, typically a transactional business service. Such a service implementation must have defined the the Collection executeSearch(SearchCriteria) method, typically by implementing a service interface:
public interface SearchService {
public Collection executeSearch(SearchCriteria criteria);
}
With this approach there are no requirements on the signature of the methods that carry out action execution, nor is there any requirement to extend from a Web Flow specific base class. Basically, you are not required to write a custom Action implementation at all--you simply instruct Spring Web Flow to call your business methods directly. The need the for custom "glue code" to bind your web-tier to your middle-tier is eliminated.
Spring Web Flow achieves this by automatically adapting the method on your existing application object to the Action interface and caring for exposing any return value in the correct scope.
This is adaption process is shown graphically below:

Bean->Action adapter
The following example constructs the equivalent action state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
...
addActionState("executeSearch", searchAction(),
transition(on("success"), to("displayResults")));
...
}
protected Action searchAction() {
AnnotatedAction searchAction =
method("executeSearch(${flowScope.criteria})", action("searchAction"));
searchAction.setResultName("results");
return searchAction;
}
}
When entered, a decision state makes a flow routing decision. This process consists of:
Evaluating one or more boolean expressions against the executing flow to decide what state to transition to next.
Alternatively, executing a single decision action and evaluating its return event to decide what state to transition to next. This action should be idempotent; that is, it should not have side effects. Convenient support for evaluating return values of methods invoked on your application objects (POJOs) is provided.
The properties of a org.springframework.webflow.DecisionState are summarized below:
Table 2.11. DecisionState properties
| Property name | Description | Cardinality | Default value |
|---|---|---|---|
| action | The idempotent action to execute when the state is entered. The action result event is used as the basis for the decision. | 0..1 | Null |
The following example constructs an DecisionState from XML that evalutes a boolean expression to determine what transition to execute:
<flow start-state="shippingRequired">
<decision-state id="shippingRequired">
<if test="${flowScope.order.needsShipping}" then="enterShippingDetails" else="placeOrder"/>
</decision-state>
...
</flow>
This state definition reads "if the needsShipping property on the order object in flow scope is true, transition to the enterShippingDetails state, otherwise transition to the placeOrder state."
Caution: flow definitions should not be vehicles for business logic. In this case the decision made was controller logic, reasoning on a pre-calculated value to decide what step of the flow to transition to next. That is the kind of logic that should be in a flow definition. In contrast, having the state itself embed the business rule defining how shipping status is calculated is a misuse. Instead, push such a calculation into application code where it belongs and instruct the flow to invoke that code using an action.
The following example constructs an DecisionState from XML that executes a action that forms the basis for the transition decision:
<flow start-state="shippingRequired">
<decision-state id="shippingRequired">
<action bean="shippingService" method="isShippingRequired"/>
<transition on="yes" to="enterShippingDetails"/>
<transition on="no" to="placeOrder"/>
</decision-state>
...
</flow>
This state definition reads "if the isShippingRequired method on the shippingService returns true, transition to the enterShippingDetails state, otherwise transition to the placeOrder state."
Note how the boolean return value of the isShippingRequired method is converted to the event identifiers yes or no.
This conversion process is handled by the action adapter responsible for adapting the method on your application object to the org.springframework.webflow.Action interface. By default, this adapter applies a number of rules for creating a result event from a method return value.
These conversion rules are:
Table 2.12. Default method return value to Event conversion rules
| Return type | Event identifier |
|---|---|
| boolean | yes or no |
| java.lang.Enum | this.name() |
| org.springframework.core.enum.LabeledEnum | this.getLabel() |
| null | null |
You may customize these default conversion policies by setting a custom EventFactory instance on the bean invoking action performing the adaption.
The following example constructs an DecisionState from XML that executes a action that invokes a method on an application object that returns a java.lang.Enum:
<flow start-state="shippingRequired">
<decision-state id="shippingRequired">
<action bean="shippingService" method="calculateShippingMethod(${flowScope.order})"/>
<transition on="BASIC" to="enterBasicShippingDetails"/>
<transition on="EXPRESS" to="enterExpressShippingDetails"/>
<transition on="NONE" to="placeOrder"/>
</decision-state>
...
</flow>
This state definition reads "if the getShippingMethod method on the shippingService returns BASIC for the current order, transition to the enterBasicShippingDetails state. If the return value is EXPRESS transition to the enterExpressShippingDetails state. If the return value is NONE transition to the placeOrder state."
When entered, a subflow state spawns another flow as a subflow.
Recall that a flow is a reusable, self-contained controller module. The ability for one flow to call another flow gives you the ability to compose independent modules together to create complex controller workflows. Any flow can be used as subflow by any other flow, and there is a well-defined contract in play by what it means to be a flow. Specifically:
A Flow is an instance of org.springframework.webflow.Flow.
A newly launched flow can be passed input attributes which it may choose to map into its own local scope.
An ending flow can return output attributes. If the ended flow was launched as a subflow, the resuming parent flow may choose to map the output attributes into its own scope.
It is helpful to think of the process of calling a flow as similiar to calling a Java method. Flows can be passed input arguments and can produce return values just like methods can. Flows are more powerful because they are potentially long-running, as they can span more than one request into the server.
The properties of a org.springframework.webflow.SubflowState are summarized below:
Table 2.13. SubflowState properties
| Property name | Description | Cardinality | Default value |
|---|---|---|---|
| subflow | The definition of the flow to be spawned as a subflow. | 1 | |
| attributeMapper | The strategy responsible for mapping input attributes to the subflow and mapping output attributes from the subflow. | 0..* | Null |
When a SubflowState is entered, the following behavior occurs:
The state first messages its attributeMapper, an instance of org.springframework.webflow.FlowAttributeMapper, to prepare a Map of input attributes to pass to the subflow.
The subflow is spawned, passed the input attributes. When this happens, the parent flow suspends itself in the subflow state until the subflow ends.
When the subflow ends, a result event is returned describing the flow outcome that occurred. The parent flow resumes back in the subflow state.
The resumed subflow state messages its attributeMapper to map any output attributes returned by the subflow into flow scope, if necessary.
Finally, the resumed subflow state responds to the result event returned by the ended subflow by matching and executing a state transition.
The constructs used in spawning a flow as a subflow is shown graphically below:

SubflowState class diagram
The following example constructs an SubflowState from XML that spawns a shipping subflow:
<flow start-state="enterOrderInformation">
...
<subflow-state id="enterShippingDetails" flow="shipping"<
<attribute-mapper>
<input-attribute value="${flowScope.order.shipping}" name="shipping"/>
</attribute-mapper>
<transition on="finish" to="placeOrder"/>
</subflow-state>
...
</flow>
This subflow state definition reads "spawn the shipping flow and pass it the value of the shipping property on the order object in flow scope as an input attribute with the name shipping. When the shipping flow ends, respond to the finish result event by transitioning to the placeOrder state."
Note how the inner structure and behavior of the shipping flow is fully encapsulated within its own flow definition. A flow calling another flow as a subflow can pass that flow input and capture its output, but it cannot see inside it. Flows are black boxes. Because any flow can be used as a subflow, it can be reused in other contexts without change.
The following illustrates the equivalent example using the FlowBuilder API:
public class OrderFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
...
addSubflowState("enterShippingDetails", flow("shipping"), shippingMapper(),
transition(on("finish"), to("placeOrder")));
...
}
protected FlowAttributeMapper shippingMapper() {
ParameterizableFlowAttributeMapper mapper = new ParameterizableFlowAttributeMapper();
mapper.addMapping("${flowScope.order.shipping}", "shipping");
return mapper;
}
}
When entered, an end state terminates a flow. A EndState represents exactly one logical flow outcome; for example, "finish", or "cancel".
If the ended flow was acting as a top-level or root flow the entire flow execution ends and cannot be resumed. In this case the end state is responsible for making a ViewSelection that is the basis for the ending response (for example, a confirmation page, or a redirect request to an external URL).
If the ended flow was acting as a subflow, the spawned subflow session ends and the calling parent flow resumes by responding to the end result returned. In this case the responsibility for any ViewSelection falls on the parent flow.
Once a flow ends any attributes in flow scope go out of scope immediately and become eligible for garbage collection.
As outlined, an end state entered as part of a root flow messages its ViewSelector to make a ending view selection. Typically this is a RedirectViewSelector, allowing for redirect after flow completion. An end state entered as part of a subflow is not responsible for a view selection; this responsibility falls on the calling flow.
When a EndState is entered it terminates a flow and, if used as subflow, returns a result event the parent flow uses to drive a state transition from the calling subflow state. It is the end state's responsibility to create this result event which is the basis for communicating the logical flow outcome to callers.
By default, an EndState creates a result event with an identifier that matches the identifier of the end-state itself. For example, an end state with id finish returns a result event with id finish. Also by default, any attributes in flow scope that have been explicitly marked as output attributes are returned as result event parameters.
Spring Web Flow gives you full control over the ending view selection strategy, as well as what flow attributes should be exposed as output on a per EndState basis. These configurable properties are summarized below:
Table 2.14. EndState properties
| Property name | Description | Cardinality | Default value |
|---|---|---|---|
| viewSelector | The strategy that makes the ending view selection when this state is entered and the flow is a root flow. | 0..1 | Null |
| outputAttributeNames | The names of attributes in flow scope that should be exposed as output; thus being made eligible for output mapping by a calling flow. | 0..* | None |
The following example constructs an EndState from XML that terminates a shipping subflow:
<flow start-state="enterShippingDetails">
...
<end-state id="finish">
<output-attribute name="shipping"/>
</end-state>
</flow>
This end state definition reads "terminate the shipping flow and expose the shipping property in flow scope as an output attribute."
Since this end-state does not make a view selection it is expected this flow will be always used as a subflow. When this flow ends, the calling parent flow is expected to respond to the finish result, and may map the shipping attribute into its own scope.
The following illustrates the equivalent example using the FlowBuilder API:
public class ShippingFlowBuilder extends AbstractFlowBuilder {
public void buildStates() {
...
addEndState("finish", new String[] { "shipping" } );
}
}
The following example constructs an EndState from XML that terminates a shipping subflow and requests a redirect response to an external URL:
<flow start-state="enterOrderInformation">
...
<end-state id="finish" view="redirect:/orders/${flowScope.order.id}"/>
</flow>
This end state definition reads "terminate the order flow and request a redirect to the URL returned by evaluating the /orders/${flowScope.order.id} expression."
This is an example of the familiar redirect after post pattern where after transaction completion a redirect is issued allowing the result of the transaction to be viewed (in this case using REST-style URLs).