Spring Faces allows you to completely replace the JSF managed bean facility with a combination of flow-managed variables and Spring managed beans. It gives you a good deal more control over the lifecycle of your managed objects with well-defined hooks for initialization and execution of your domain model. Additionally, since you are presumably already using Spring for your business layer, it reduces the conceptual overhead of having to maintain two different managed bean models.
In doing pure JSF development, you will quickly find that request scope is not long-lived enough for storing conversational model objects that drive complex event-driven views. The only available option is to begin putting things into session scope, with the extra burden of needing to clean the objects up before progressing to another view or functional area of the application. What is really needed is a managed scope that is somewhere between request and session scope. Fortunately web flow provides such extended facilities.
The easiest and most natural way to declare and manage the model is through the use of flow variables . You can declare these variables at the beginning of the flow:
<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>
and then reference this variable in one of the flow's JSF view templates through EL:
<h:inputText id="searchString" value="#{searchCriteria.searchString}"/>
Note that you do not need to prefix the variable with its scope when referencing it from the template (though you can do so if you need to be more specific). As with standard JSF beans, all available scopes will be searched for a matching variable, so you could change the scope of the variable in your flow definition without having to modify the EL expressions that reference it.
You can also define view instance variables that are scoped to the current view and get cleaned up automatically upon transitioning to another view. This is quite useful with JSF as views are often constructed to handle multiple in-page events across many requests before transitioning to another view.
To define a view instance variable, you can use the
var
element inside a
view-state
definition:
<view-state id="enterSearchCriteria"> <var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/> </view-state>
Though defining autowired flow instance variables provides nice modularization and readability, occasions may arise where you want to utilize the other capabilities of the Spring container such as AOP. In these cases, you can define a bean in your Spring ApplicationContext and give it a specific web flow scope:
<bean id="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria" scope="flow"/>
The major difference with this approach is that the bean will not be fully initialized until it is first accessed via an EL expression. This sort of lazy instantiation via EL is quite similar to how JSF managed beans are typically allocated.
The need to initialize the model before view rendering (such as by loading persistent entities from a database) is quite common, but JSF by itself does not provide any convenient hooks for such initialization. The flow definition language provides a natural facility for this through its Actions . Spring Faces provides some extra conveniences for converting the outcome of an action into a JSF-specific data structure. For example:
<on-render> <evaluate expression="bookingService.findBookings(currentUser.name)" result="viewScope.bookings" result-type="dataModel" /> </on-render>
This will take the result of the
bookingService.findBookings
method an wrap it in a custom JSF DataModel so that the list can be used in a standard JSF DataTable
component:
<h:dataTable id="bookings" styleClass="summary" value="#{bookings}" var="booking" rendered="#{bookings.rowCount > 0}"> <h:column> <f:facet name="header">Name</f:facet> #{booking.hotel.name} </h:column> <h:column> <f:facet name="header">Confirmation number</f:facet> #{booking.id} </h:column> <h:column> <f:facet name="header">Action</f:facet> <h:commandLink id="cancel" value="Cancel" action="cancelBooking" /> </h:column> </h:dataTable>
The custom DataModel provides some extra conveniences such as being serializable for storage beyond request scope and access to the currently selected row in EL expressions. For example, on postback from a view where the action event was fired by a component within a DataTable, you can take action on the selected row's model instance:
<transition on="cancelBooking"> <evaluate expression="bookingService.cancelBooking(bookings.selectedRow)" /> </transition>