http://www.theserverside.com/articles/article.tss?l=SpringWebFlow

SpringWebFlow.gif

소개#

좀더 복잡하게 된 당신의 웹애플리케이션에서 페이지흐름을 이해하고 관리해본적 있는가.? 당신은 재사용을 하지 않는 것을 수행하는 특별한 방법에 집중해서 지쳤는가.? 세션 상태관리처럼 일반적인 문세를 위해 당신 자신만의 접근법을 개발하기 위해 너무 많은 시간을 소비한다고 느끼지는 않은가.?

Spring Web Flow를 사용하라.

Spring Web Flow는 무엇인가.?#

Spring Web Flow(SWF)는 Spring프레임워크에서 새로 생성된 모듈이다. 이 모듈은 Spring MVC를 포함하는 Spring의 웹애플리케이션 개발 스택에 일부이다.

Spring Web Flow는 웹애플리케이션 페이지의 흐름을 관리하기 위한 가장 훌륭한 해결법이 되는것을 목표로 하고 있다. 이것은 애플리케이션이 커다란 애플리케이션 트랜잭션내에 각각의 단계를 통해 사용자를 가이드하기 위한 마법사처럼 복잡하게 제어되는 탐색(navigations)을 요구할때 사용하기 위한 강력한 컨트롤러이다.

그런 제어되는 탐색(navigation)의 예제를 밑의 UML상태 다이어그램처럼 설명한다.

clip_image001.gif

Figure 1 - 항공권 예약 흐름

영리한 독자는 당신이 온라인 항공권 예약을 할때마다 참여하는것과 같은 전형적인 항공권 예약 흐름처럼 이것을 인식할것이다.

왜 Spring Web Flow가 존재하는가.?#

예를 들어 Struts로 웹애플리케이션을 빌드하자. Strut내에 페이지흐름을 구현하기 위해서 대부분의 개발자는 그것들(액션과 뷰)을 지원하는 프레임워크 위에서 개발할것이다. 이런 경우에 한개의 액션은 특정 요청 URL과 함께 포함된다. 요청이 URL에 도달했을때 액션은 수행된다. 수행되는 동안 액션은 몇몇의 처리를 수행하고 보여주기 위해 선호하는 형식의 결과뷰를 선택한다. 이것은 간단하다.

그럼 Struts내의 다중단계의 페이지흐름을 구현하기 위해서 개별적인 액션들은 다양한 뷰를 통해 함계 연결된다. "back"또는 "submit"와 같은 각각의 다른 이벤트를 처리하기 위한 액션 URL은 각각의 뷰로 하드코딩된다. 당면한 세션 저장소의 몇몇 형태는 흐름 상태를 관리하기 위해서 사용된다. 포스트(post)이후에 리다이렉트는 이중서브밋등을 막기 위해서 사용된다.

이것은 간단하고 편리한 접근법임에도 불구하고 웹애플리케이션의 전체적인 페이지흐름이 struts-config.xml파일내의 액션정의에서 찾음으로써 깔끔하지 않다는 커다란 단점을 가진다. 많은 액션과 뷰정의인 나무들로부터 흐름인 숲을 볼수는 없다. 유연성 역시 액션들과 뷰들이 쉽게 재사용할수 없기 때문에 고민이 된다. 마침내 당신은 좀더 쉬워야 할 일을 매우 어렵게 하게 된다.

Spring MVC는 미리정의된 페이지흐름을 구현하는 컨트롤러로 부터 조금은 더 높은 레벨의 기능을 제공한다. SimpleFormController 와 AbstractWizardFormController의 두가지 컨트롤러가 제공된다. 어쨌든 좀더 일반적인 페이지흐름의 개념 예제를 설명하고 있다.

Tapestry 와 JSF는 요청레벨보다는 각각의 페이지와 그것을 지지하는 컨트롤러 로직은 함께 유지되는 페이지레벨에서 이벤트지향 접근법을 사용한다. 어쨌든 어느것도 다양한 페이지와 임시의 서로 다른 경로를 확장하는 잘 정의된 생명주기와 함께 지역적인 페이지흐름을 위한 최상의 지원을 제공하지 않는다. 당신이 볼수 있는것처럼 페이지흐름과 같은 생명주기는 한개의 요청보다는 더 길지만 세션보다는 짧다.

이것은 Struts, Spring MVC, Tapestry, JSF, 그리고 어떠한 Portlets 같은 환경을 포함하는 어디에서든 깔끔하고 간단한 방법으로 웹애플리케이션의 페이지흐름을 표현하도록 하고 이것을 재사용하도록 하는 Spring Web Flow가 위치하는 곳이다.

장점#

당신이 보는것처럼 Spring Web Flow는 다양한 장점을 제공한다.

  • 웹애플리케이션내 페이지흐름은 관련된 웹흐름정의(XML파일이나 자바클래스)에서 찾음으로써 깔끔하게 볼수 있다.
  • 웹흐름은 스스로를 포함하도록 디자인되었다. 이것은 당신에게 다양한 위치에서 재사용할수 있는 모듈처럼 당신의 애플리케이션의 일부를 볼수 있도록 한다.
  • 웹흐름은 같은 일관성있는 기술을 사용해서 언제나 웹애플리케이션내에서 적절한 페이지흐름을 획득한다. 당신은 매우 특정한 상황을 위해 특정한 컨트롤러를 사용해서 집중하지는 않는다.
  • 마지막으로 웹흐름은 사용하기 위해 잘 정의된 계약과 함께 최상급의 선택이다. 이것은 당신을 위해 자동적으로 관리되는 깔끔하고 관찰가능한 생명주기를 가진다. 간단하게 두면 시스템은 당신을 위해 복잡함으로 관리하고 결과로써 사용하기 쉽게 한다.

Spring Web Flow는 어떻게 작동을 하는가.?#

지금은 웹흐름이 상태의 집합으로 구성된다고 말하는것만으로도 충분하다. 상태는 페이지내의 뭔가가 발생하는 지점(point)이다. 예를 들면 뷰를 표현하거나 액션을 수행하는 것을 말한다. 각각의 상태는 다른 상태로 이동하는 한개이상의 전환(transitions)을 가진다.

전환(transition)은 이벤트에 의해 유발된다.

예약 항공권 샘플 웹흐름#

웹흐름정의가 보이는것처럼 시연하기 위해서 다음의 XML의 일부는 위 UML상태다이어그램내에서 설명된 항공권 예약 처리를 획득한다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
  "http://www.springframework.org/dtd/spring-webflow.dtd">

<webflow id="bookflight" start-state="obtainTripInfo">

  <action-state id="obtainTripInfo">
    <action bean="bookingActions" method="bindAndValidate"/>
    <transition on="success" to="suggestItineraries"/>
    <transition on="error" to="tryAgain"/>
  </action-state>

  <action-state id="suggestItineraries">
    <action bean="bookingActions"/>
    <transition on="success" to="displaySuggestedItineraries"/>
  </action-state>

  <view-state id="displaySuggestedItineraries" view="suggestedItenaries">
    <transition on="startOver" to="cancel"/>
    <transition on="select" to="selectItinerary"/>
  </view-state>

  <action-state id="selectItinerary">
    <action bean="bookingActions"/>
    <transition on="success" to="isPassengerInfoRequired"/>
  </action-state>

  <decision-state id="isPassengerInfoRequired">
    <if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
    <if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
      then="enterPassengerInformation" else="displayReservationVerification"/>
  </decision-state>

  <subflow-state id="enterPassengerInformation" flow="passenger">
    <attribute-mapper>
      <input value="${requestScope.passenger.id}" as="passengerId"/>
    </attribute-mapper>
    <transition on="finish" to="displayReservationVerification"/>
  </subflow-state>

  <view-state id="displayReservationVerification" view="reservationVerification">
    <transition on="startOver" to="cancel"/>
    <transition on="assignSeats" to="chooseSeatAssignments"/>
    <transition on="book" to="book"/>
  </view-state>

  <subflow-state id="chooseSeatAssignments" flow="seatAssignments">
    <attribute-mapper>
      <input value="${requestScope.passenger.id}" as="passengerId"/>
      <input name="itinerary"/>
    </attribute-mapper>
    <transition on="finish" to="reservationVerification"/>
  </subflow-state>

  <action-state id="book">
    <action bean="bookingActions"/>
    <transition on="success" to="displayConfirmation"/>
  </action-state>

  <end-state id="displayConfirmation" view="reservationConfirmation"/>

  <end-state id="tryAgain" view="tryAgain"/>

  <end-state id="cancel" view="home"/>

</webflow>

Figure 2 - XML기반의 항공권 예약 흐름정의

당신이 볼수 있는것처럼 XML정의를 유심히 살펴봄으로써 예약 처리를 다루는 논리적인 흐름은 당신이 아직 Spring Web Flow구현사항에 대해서 알지 못하더라도 명백하게 인지할수 있다.

당신이 조금더 가까이에서 본다면 당신은 예약 흐름의 자식 처리를 유발하는 두개의 하위흐름을 볼것이다. 첫번째 하위흐름은 승객정보를 입력함으로써 사용자를 안내한다. 두번째는 사용자에게 좌석을 할당한다. "작은 애플리케이션 모듈"처럼 작동하는 흐름을 포괄하기 위한 능력은 Spring Web Flow의 가장 강력한 기능중에 하나이다.

당신은 비지니스 분석을 위해 정의를 보여줄수 있고 그것을 얻을수 있을것이다. 당신은 이 정의로 부터 시각적인 다이어그램을 다룰수 있고 재조사를 위해 비지니스 분석을 제공할수 도 있다.

설명되는 항공권 예약 흐름#

이 글의 다음 파트는 위 항공권 예약 정의의 키부분을 분류하고 Spring Web Flow가 작동하는 방법을 설명하는 다이얼로그를 제공한다.

흐름 정의(Flow Definition)

XML기반의 흐름정의의 1번 라인부터 시작하자.

<webflow id="bookflight" start-state="obtainTripInfo">
  ...
</webflow>

webflow요소는 흐름과 그것의 id그리고 시작상태(start-state)를 정의한다. id는 간단하게 유일한 식별자이다. 시작상태는 실행시 새로운 흐름 세션이 활성화되었을때 전환하기 위해 첫번째 상태이다.

이러한 비지니스 상황을 위해서 새로운 항공권예약 세션이 활성화 되었을때 이것은 obtainTripInfo상태로 전환된다.

여행 정보 액션 상태얻기

obtainTripInfo상태정의로 옮기자.

<action-state id="obtainTripInfo">
  <action bean="bookingActions" method="bindAndValidate"/>
  <transition on="success" to="suggestItineraries"/>
  <transition on="error" to="tryAgain"/>
</action-state>

상태에 들어왔을때 다시 호출하는 것은 행위를 발생시킨다. 당신이 보는것처럼 다른 행위를 수행하는 서로 다른 상태타입이 있다. obtainTripInfoabove과 같은 액션 상태는 들어왔을때 액션을 수행한다. 그 액션은 이것의 논리적 수행결과를 반환하고 결과는 상태전환으로 맵핑된다. 이것은 간단하다.

obtainTripInfo라는 비지니스 예를 위해 들어왔을때 bookingActions식별자와 함께 액션 구현의 bindAndValidate메소드를 수행한다. 이 메소드는 도메인객체를 여행하고 그것을 체크하기 위해 브라우저로 부터 폼입력을 묶는다. 만약에 처리가 성공한다면 suggestItineraries상태로 들어가고 에러가 발생하면 tryAgain상태로 들어간다.

예약 액션

Spring IoC와 함께 Spring Web Flow를 사용할때 action요소의 bean속성은 Spring애플리케이션 컨텍스트내로 보내진 액션 구현의 이름을 참조한다. 여기에 bookingActions빈정의는 이것과 비슷하게 보인다.

web-context.xml

<bean id="bookingActions" class="org.springframework.samples.bookflight.BookingActions">
    <property name="bookingAgent" ref="myBookingAgent"/>
</bean>

이것은 Spring에 의해 관리되기 위해 우리의 액션구현을 허락하고 DI(Dependency Injection)를 통해 설정된다.

여정액션 상태 제안하기

지금 입력처럼 주어진 범위와 체크된 여행객체, 제안된 여정의 컬렉션을 반환하는 다음 액션상태를 보자.

<action-state id="suggestItineraries">
  <action bean="bookingActions"/>
  <transition on="success" to="displaySuggestedItineraries"/>
</action-state>

이것이 발생하도록 요구되는 액션 구현코드는 간단하다.

public class BookingActions extends FormAction {
    ...
    public Event suggestItineraries(RequestContext context) {
        Trip trip = (Trip)context.getRequestScope().getAttribute("trip");
        Collection<Itinerary> itineraries = bookingAgent.suggestItineraries(trip);
        context.getRequestScope().setAttribute("itineraries", itineraries);
        return success();
    }
}

suggestItineraries상태로 들어왔을때 suggestItineraries메소드는 호출된다. 목표 액션빈에서 메소드를 호출하는 상태로 들어가는 다른 액션상태는 정확하게 같은 방법으로 작동한다.

제안된 여정 뷰상태 표현하기

제안된 여정의 컬렉션이 반환되었을때 다음 단계는 가장 좋은것을 선택하도록 사용자 재검사를 가진다. 이것은 다음의 상태정의에 의해 달성된다.

<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
  <transition on="startOver" to="cancel"/>
  <transition on="select" to="selectItinerary"/>
</view-state>

당신이 볼수 있는것처럼 displaySuggestedItineraries는 우리가 아직 논의하지 않은 상태타입인 뷰상태이다. 뷰상태가 들어왔을때 정지하기 위해 수행흐름을 야기하고 설정된 뷰를 표현하기 위해 지시와 함께 클라이언트에 제어를 반환한다. 흐름을 다시 시작하고 발생된 이벤트는 흐름내에 다른 단계로 사용자를 이끄는 상태전환을 위해 맵핑한다. 다시 말해서 이것은 간단하다.

이 비지니스경우를 위해서 displaySuggestedItineraries상태가 suggestedIteneraries뷰로 들어갔을때 브라우저로 표현되고 제어를 반환한다. 사용자가 원하고 "select"버튼을 클릭하는것을 결정한다. 그 신호는 select이벤트이다. 이벤트 파라미터처럼 선택된 여정의 id를 넘긴다.

사용자는 cancel상태로 흐름을 전환하는 시점에 startOver를 선택할수도 있다.

클라이언트 환경의 책임은 /WEB-INF/jsp/suggestedIternaries.jsp처럼 표현가능한 뷰 템플릿을 위해 suggestedItineraries처럼 요청된 뷰이름을 맵핑하기 위해 사용된다. 예를 들면 Spring MVC내에서 FlowController은 친숙한 ModelAndView 와 ViewResolver생성자를 사용해서 이것을 한다. Struts에서 FlowAction은 친숙한 ActionForward을 사용함으로써 이것을 한다.

클라이언트 측 상태

당신이 요청하는 이 시점에서

"수행흐름이 ViewState로 들어왔을때 잠시 멈춘다. 그리고 제어는 브라우저로 반환된다. 어떻게 같은 흐름을 가지고 하위 이벤트에서 다시 실행되는가.?"

이 답은 클라이언트가 흐르을 수행하는 유일한 tracks를 추적하고 다음 이벤트가 신호화되었을때 입력처럼 그것을 제공한다. 이것은 hidden폼필드를 사용한 일반적인 방법이다.

예를 들면 jsp에서

<input type="hidden" value="<c:out value="${flowExecution.id}"/>">

"요구된 승객정보인가.?" 결정(Decision)상태

사용자가 원하는 여정을 선택한 후 흐름은 다음에 가야할 지점에 대해 개념적인 결정사항을 가져야만 한다.

특별히 사용자가 로그인하지 않았거나 로그인 했지만 사용할 신용카드정보와 같은 승객정보를 확인하고자 한다면 흐름은 그러한 정보를 입력하도록 허락할것이다. 반면에 이미 로그인한 상태이고 바로 예약 페이지로 가길 원한다면 흐름은 옵션적인 단계를 건너뛸것이다.

기본적으로 동적인 결정은 사용자의 정보와 선호사항이 되도록 한다.

결정상태는 이것을 위해 완벽하다. 다음의 정의를 보라.

<decision-state id="isPassengerInfoRequired">
  <if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
  <if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
    then="enterPassengerInformation" else="displayReservationVerification"/>
</decision-state>

승객정보 하위흐름(SubFlow)상태 들어가기

승객정보를 관리하는 처리는 논리적으로는 예약처리와는 독립적이다. 이것은 처리내의 하나의 파트이지만 이것은 예약화면밖에서 사용자가 정보를 수정하길 원한다고 이해할것이다.

하위흐름(SubFlow)상태는 이것을 촉진한다. 하위흐름 상태로 들어왔을때 자식(child) 흐름은 유발한다. 부모(parent)흐름은 자식 흐름이 종료될때까지 일시정지한다. 이것은 당신에게 자신포함(self-contained)모듈의 집합처럼 당신의 애플리케이션을 보도록 만든다. 당신은 일관적인 방법으로 다양한 상황내에 쉽게 넣을수 있다.

enterPassengerInformation하위흐름 상태를 살펴보자.

<subflow-state id="enterPassengerInformation" flow="passenger">
<attribute-mapper>
    <input value="${requestScope.passenger.id}" as="passengerId"/>
  </attribute-mapper>
  <transition on="finish" to="displayReservationVerification"/>
</subflow-state>

flow속성은 이 상태에 들어왔을때 유발하기 위한 흐름의 id이다. attribute-mapper요소는 하위흐름으로 부터 그리고 하위흐름으로 속성값을 맵핑시킨다. input맵핑은 하위흐름으로 속성을 맵핑시킨다. output맵핑은 하위흐름이 종료되었을때 부모흐름으로 속성을 맵핑시킨다. 당신이 볼수 있는것처럼 표현(expression)또한 제공된다.

이러한 비지니스 경우를 위해서 enterPassengerInformation상태로 들어왔을때 passenger흐름은 유발된다. passengerId속성은 입력처럼 흐름에 전달된다. 여기서부터 하위흐름은 그것이 원하는것을 한다. 이것은 부모흐름이 걱정하는 것만큼 블랙박스이다. 하위흐름이 종료되었을때 부모흐름은 다시 시작되고 예약확인을 위해 이런경우에 다음에 가야할 지점을 결정하기 위해 종료결과에 반응한다.

확인 종료 상태를 보여주기

여기에 논의가 되어야 할 마지막 핵심상태(종료(end)상태) 타입이 하나 있다. 종료상태에 들어왔을때 활성화된 흐름세션은 제거된다. 제거함으로써 흐름에 관련된 모든 자원은 자동적으로 당신을 위해서 말끔하게 사라진다.

아래는 여정이 성공적으로 예약이 된 후에 확인사항을 보여주는 종료상태의 displayConfirmation이다.

<end-state id="displayConfirmation" view="reservationConfirmation"/>

이 상태에 들어왔을때 bookflight흐름은 종료되고 reservationConfirmation뷰를 보여준다. bookflight흐름이 하위흐름이 아닌 가장상위 흐름처럼 작동하기 때문에 이것과 어떤 할당된 자원은 자동적으로 없어진다.

주의: had the ending flow been acting as a subflow, the entered end state is treated as a subflow result the resuming parent flow can respond to. More specifically, the entered end state ID is used as grounds for a state transition in the resuming parent flow's subflow state. You can see this in action by taking a look at the "enterPassengerInformation" subflow state definition. Note how it responds to the "finish" result of the subflow, which corresponds to a "finish" end state within the passenger flow.

흐름 배치#

당신은 Spring Web Flow가 무엇인지 모든것을 배웠고 실제 흐름정의의 예제를 볼것이다. 당신이 보지 못한것은 서블릿 환경에서 Spring MVC처럼 특정 환경내에서 실행을 위해 흐름정의를 배치하는 방법이다.

여기의 모든것은 Spring MVC와 함께 사용해야 한다.

<bean name="/booking.htm" class="org.springframework.web.flow.mvc.FlowController">
    <property name="flow">
        <ref bean="bookingFlow"/>
    </property>
</bean>
 
<bean id="bookingFlow" class="org.springframework.web.flow.config.XmlFlowFactoryBean">
   <property name="location" value="classpath:bookflight-flow.xml"/>
</bean>

이것은 서블릿환경내에서 사용을 위한 /booking.html URL에 bookingFlow를 자동적으로 보낸다.

좀더 향상된 주제#

다음의 섹션은 Spring Web Flow의 좀더 향상된 기능을 소개한다.

흐름 수행 리스너

FlowExecutionListener생성은 흐름을 수행하는 생명주기를 듣고 반응하도록 하는 관찰자이다. 당신은 감시와 보안을 위해 상태필수조건과 조건체크로 부터 어떤것을 하도록 이 기능을 사용할수 있다.

흐름 수행 저장소 전략

저장되고 복구되는 흐름을 수행하는 상태에 의한 구조는 완전히 접속가능하다. HttpSession기반의 저장소가 디폴트이고 SWF는 다른 두가지의 저장소 전략을 제공한다. 하나는 서버측의 지속적인 세션저장소이고 다른 하나는 완전히 클라이언트측 직렬화를 사용한다. 예를 들어 데이터베이스내에 흐름상태를 저장하는것과 같이 당신의 사용자정의 저장소를 정의하는것은 좋지않다(trivial).

Spring Web Flow는 언제 당신을 위해 사용할까.?#

Spring Web Flow는 하나의 크기에 모든것을 만족하는 솔루션이 아니라는것에 주의해야만 한다. 당신이 보는것처럼 이것은 비지니스 프로세스를 다루는 페이지흐름 관리를 자동적으로 하는 상태유지 시스템이다. 이것은 좀더 간단하고 비상태유지 솔루션이 좀더 선호되는 경우에는 사용되지 말아야 한다. 예를 들면 자유로운 탐색을 요구하거나 사용자가 원하는 어느 지점이든 클릭을 할수 있는 사이트라면 사용하지 말야야 한다. Spring Web Flow는 깔끔한 비지니스 목표와 생명주기를 가진 프로세스를 통해 인도된 사용자가 있는 곳에서 강력하게 제어되는 탐색을 위해서 디자인되었다.

좀더 견고한 경우에 사용하도록 하기 위해서 여기에 SWF시스템이 선호하는 "good flows" 라는 예제가 있다.

  • Book a flight
  • Pay your taxes
  • Apply for a loan

여기에 Spring Web Flow이 선호하지 않는 예제가 있다.

  • Index pages
  • Welcome pages
  • Menus
  • Simple form flows (one page)

Spring Web Flow는 어떤 웹환경내에서 기존의 Spring MVC, Struts, Tapestry, Web Work, JSF, 또는 Portlets과 같은 컨트롤러에 선물을 주는것처럼 사용되는것을 의미한다.하나의 사이트는 선호되는 지점의 웹흐름과 함께 간단한 컨트롤러로 조합될수 있다.

로드맵#

Spring Web Flow 1.0의 정식버전은 Spring1.3과 함께 릴리즈될것이다. 지금과 그 시기 사이에 일반적이고 개발목적의 사용으로는 안정적인 프리뷰버전을 기대한다. 개발팀은 정식릴리즈전에는 닫혀있을것이고 여기에 우리가 작업 할 대부분의 가장 중요한 기능이 있다.

통합

독립적인 라이브러리처럼 Spring Web Flow는 다른 프레임워크와 강력하게 통합될것이다. Spring MVC, Struts, 그리고 Portlet MVC와의 통합은 이미 제공된다. JSF와 Tapestry와의 통합은 정식버전에서 기대된다.

흐름 관리

Spring 1.2에서 관리와 모니터링을 위한 MBeanServer내에 빈즈를 보내는것은 쉽다. 강력하게 정의된 FlowExecutionMBean관리 인터페이스는 벌써 존재하고 서버에서 수행되는 모든 흐름의 전역적인 통계가 JMX콘솔을 통해 중심적으로 모니터링될수 있도록 확장할 계획이다.

Pluggability

시스템 내의 모든 생성은 xml정의에서 조차도 쉬운 확장과 커스터마이징을 위해 접속가능(pluggable)하게 될것이다. 이것은 다른 개념들 사이에 상태와 전환을 포함한다.

트랜잭션 보정하기

제공되는 기능과 예제 애플리케이션은 흐름의 수행시간동안 먼저 커밋된 작업을 롤백하기 위해 트랜잭션 보정의 사용을 설명하고 있다.

결론#

Spring Web Flow는 비지니스 프로세스를 다루는 제어된 탐색을 관리하기 위해 아주 강력한 솔루션이다. 그리고 이것은 작업하기에 재밌다. 만약 당신이 여전이 이것을 사용하지 않는다면 당신은 무엇을 기다리는가.?

레퍼런스#

Spring Web Flow는 Interface21에 의해 제공되는 핵심 Spring훈련 과정내에서 다루어진다. - http://www.springframework.com/training

The Spring Framework, http://www.springframework.org

The Spring Web Flow Wiki, http://opensource.atlassian.com/confluence/spring/display/WEBFLOW/Home

The kdonald blog, http://www.jroller.com/page/kdonald

Struts, http://struts.apache.org

Java Server Faces, http://java.sun.com/j2ee/javaserverfaces/

Tapestry, http://jakarta.apache.org/tapestry

WebWork, http://www.opensymphony.com/webwork/

JMX, http://java.sun.com/jmx

JavaOne, http://java.sun.com/javaone/

Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
gif
SpringWebFlow.gif 11.3 kB 1 06-Apr-2006 09:45 218.239.69.220
gif
clip_image001.gif 7.4 kB 1 06-Apr-2006 09:45 218.239.69.220
« This page (revision-1) was last changed on 06-Apr-2006 09:45 by UnknownAuthor