<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <!-- Scans for application @Components to deploy -->
    <context:component-scan base-package="org.springframework.webflow.samples.booking" />

    <!-- Imports the configurations of the different infrastructure systems of the application -->
    <import resource="webmvc-config.xml" />
    <import resource="webflow-config.xml" />
    <import resource="data-access-config.xml" />
    <import resource="security-config.xml" />

</beans>

web.xml에 유일하게 등록되어 있는 빈 설정파일이었는데, 열어보니 네 개의 다른 빈 설정 파일들을 import 하고 있습니다. 이 중에서 webmvc-config.xml 파일을 보겠습니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- Maps request paths to flows in the flowRegistry; e.g. a path of /hotels/booking looks for a flow with id "hotels/booking" -->
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
        <property name="order" value="0" />
    </bean>

    <!-- Maps request paths to @Controller classes; e.g. a path of /hotels looks for a controller named HotelsController -->
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
        <property name="order" value="1" />
        <property name="defaultHandler">
            <!-- If no @Controller match, map path to a view to render; e.g. the "/intro" path would map to the view named "intro" -->   
            <bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
        </property>
    </bean>

    <!-- Resolves logical view names returned by Controllers to Tiles; a view name to resolve is treated as the name of a tiles definition -->
    <bean id="tilesViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.webflow.mvc.view.FlowAjaxTilesView"/>
    </bean>

    <!-- Configures the Tiles layout system -->
    <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/layouts/layouts.xml</value>
                <value>/WEB-INF/views.xml</value>
                <value>/WEB-INF/hotels/views.xml</value>
                <value>/WEB-INF/hotels/booking/views.xml</value>
            </list>
        </property>
    </bean>
   
    <!-- Dispatches requests mapped to POJO @Controllers implementations -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

    <!-- Dispatches requests mapped to org.springframework.web.servlet.mvc.Controller implementations -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

    <!-- Dispatches requests mapped to flows to FlowHandler implementations -->
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
        <property name="flowExecutor" ref="flowExecutor"/>
    </bean>

    <!-- Custom FlowHandler for the hotel booking flow -->
    <bean name="hotels/booking" class="org.springframework.webflow.samples.booking.BookingFlowHandler" />   
       
</beans>

1. org.springframework.webflow.mvc.servlet.FlowHandlerMapping

이 클래스는 (FlowUrlHandler를 사용하여) URL을 분석하여 해당 URL을 처리할 FlowHandler를 찾아서 반환해주는 역할을 합니다.

먼저, ApplicationContext에서 찾고, 다음은 FlowRegistry에서 찾아보고, 그래도 없으면 null을 반환합니다. 그럼 이제 이 다음 핸들러 맵핑 체인을 타게 되겠죠.

따라서 이 클래스에 설정할 수 있는 주요 속성은 FlowUrlHandler와 FlowRegistry입니다. 하지만 이중에서 FlowUrlHandler는 스프링이 기본으로 제공하는 DefaultFlowHandler를 사용하는게 일반적일 것 같군요.

2. org.springframework.webflow.mvc.servlet.FlowHandlerAdapter

이 녀석은 FlowHandler가 담당하기로 한 요청을 FlowExecutor를 이용해서 처리하는 일련의 워크 플로우에 따라 실행시켜주는 곳입니다. 실질적인 작업들은 상당 부분을 위임하고 있으면서 그 골격을 만들어둔 곳입니다.

플로우 id에 해당하는 플로우 정의를 가져오고, 플로우 정의로 플로우 실행 객체(FlowExecution)을 만들고, FlowUrlHandler로 필요한 execution 매개변수도 만들는 등.. 한 번의 코드리뷰로 파악하기에는 분량이 많더군요. 특히 FlowExecutor 쪽으로 들어가면.. 크헉.. 클래스가 팍팍 늘어납니다.

3. org.springframework.webflow.mvc.servlet.FlowHandler

특정 플로우 정의에 접근하는 것을 커스터마이징 할 때 이 녀석을 구현한다고 합니다.
- Launch executions of that flow with data in the execution input map
- 플로우 결과는 맘대로 처리하고 싶을 때.
- 플로우에서 다루지 않은 예외를 맘대로 다루고 싶을 때.

위 예제에서 BookingFlowHandler 이녀석이 구현한 건 두 개.
- String handleExecutionOutcome: 실행이 끝났을 때 갈 위치
- String handleException: 에러가 발생했을 때 갈 위치