📄 formaction.java
字号:
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.action;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.binding.format.support.LabeledEnumFormatter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.PropertyEditorRegistrar;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.webflow.Event;
import org.springframework.webflow.RequestContext;
import org.springframework.webflow.ScopeType;
import org.springframework.webflow.util.DispatchMethodInvoker;
/**
* Multi-action that implements common logic dealing with input forms.
* <p>
* Several action execution methods are provided:
* <ul>
* <li> {@link #exposeFormObject(RequestContext)} - Loads the backing form object
* and exposes it and an empty errors instance in the model of the executing flow
* in the correct scope. Any custom property editors for formatting
* form object values will also be installed. This action method will return success()
* if the form object was loaded successfully, error() otherwise.
* <li> {@link #setupForm(RequestContext)} - Prepares the backing form object for display
* on a form. This method behaves exactly like exposeFormObject but goes further
* by adding a capability to perform optional data binding on setup. This action
* method will return the success() event if there are no setup errors, otherwise it
* will return the error() event.
* </li>
* <li> {@link #bindAndValidate(RequestContext)} - Binds all incoming event
* parameters to the form object and validates the form object using a
* registered validator. This action method will return the success()
* event if there are no binding or validation errors, otherwise it will return
* the error() event.
* </li>
* <li> {@link #bind(RequestContext)} - Binds all incoming event
* parameters to the form object. No additional validation is performed.
* This action method will return the success() event if there are no binding
* errors, otherwise it will return the error() event.
* </li>
* <li> {@link #validate(RequestContext)} - Validates the form object using a
* registered validator. No data binding is performed. This action method will
* return the success() event if there are no validation errors, otherwise it
* will return the error() event.
* </li>
* <li> {@link #resetForm(RequestContext)} - Resets the form by reloading
* the backing form object and reinstalling any custom property editors.
* Returns success() on completion, error() if a form object load failure occurs.
* </li>
* </ul>
* <p>
* Since this is a multi-action, a subclass could add any number of additional
* action execution methods, e.g. "setupReferenceData(RequestContext)", or
* "processSubmit(RequestContext)".
* <p>
* Using this action, it becomes very easy to implement form preparation and
* submission logic in your flow. One way to do this follows:
* <ol>
* <li> Create an view state to display the form. In an entry action of that
* state, invoke {@link #setupForm(RequestContext) setupForm} to prepare the
* new form for display. </li>
* <li> On submit, execute a state transition action that performs a bindAndValidate.
* This will invoke {@link #bindAndValidate(RequestContext) bindAndValidate} to
* bind incoming event parameters to the form object and validate the form object.
* <li>If there are binding or validation errors, the transition will not be allowed
* and the view state will automatically be re-entered.
* <li> If binding and validation is successful, go to an action state called
* "processSubmit" (or any other appropriate name). This will invoke an action method
* called "processSubmit" you must provide on a subclass to process form submission,
* e.g. interacting with the business logic. </li>
* <li> If business processing is ok, continue to a view state to display the
* success view. </li>
* </ol>
* <p>
* Here is an example implementation of such a compact form flow:
* <pre>
* <view-state id="displayCriteria" view="searchCriteria">
* <entry>
* <action bean="searchFormAction" method="setupForm"/>
* </entry>
* <transition on="search" to="executeSearch">
* <action bean="searchFormAction" method="bindAndValidate"/>
* </transition>
* </view-state>
*
* <action-state id="executeSearch">
* <action bean="searchFormAction"/>
* <transition on="success" to="displayResults"/>
* </action-state>
*</pre>
* </p>
* <p>
* When you need additional flexibility, consider splitting the view state above
* acting as a single logical <i>form state</i> into multiple states. For example,
* you could have one action state handle form setup, a view state trigger
* form display, another action state handle data binding and validation, and another
* process form submission. This would be a bit more verbose but would also give you
* more control over how you respond to specific results of fine-grained actions that
* occur within the flow.
* <p>
* <b>Subclassing hooks:</b>
* <ul>
* <li>An important hook method provided by this class is
* {@link #initBinder(RequestContext, DataBinder) initBinder}. This is
* called after a new data binder is created by any of the action execution methods.
* It allows you to install any custom property editors required to format richly-typed
* form object property values.
* <li>Another important hook is {@link #loadFormObject(RequestContext) loadFormObject}.
* You may override this to customize where the backing form object comes from
* (e.g instantiated directly in memory or loaded from a database).
* </ul>
* <p>
* Note that this action does not provide a <i>referenceData()</i> hook method
* similar to that of Spring MVC's <code>SimpleFormController</code>. If you need to
* expose reference data to populate form drop downs for example, you should create
* a custom action method in your FormAction subclass that does just that, and invoke it
* as either a chained action as part of a form setup state, or as a fine grained state
* definition itself.
* <p>
* For example, you might create this method in your subclass:
* <pre>
* public Event setupReferenceData(RequestContext context) throws Exception {
* Scope requestScope = context.getRequestScope();
* requestScope.setAttribute("refData", referenceDataDao.getSupportingFormData());
* return success();
* }
* </pre>
* <p>
* <b>FormAction configurable properties</b><br>
* <table border="1">
* <tr>
* <td><b>name</b></td>
* <td><b>default</b></td>
* <td><b>description</b></td>
* </tr>
* <tr>
* <td>formObjectName</td>
* <td>"formObject"</td>
* <td>The name of the form object. The form object will be
* set in the configured scope using this name. </td>
* </tr>
* <tr>
* <td>formObjectClass</td>
* <td>null</td>
* <td>The form object class for this action. An instance of this class will
* get populated and validated. </td>
* </tr>
* <tr>
* <td>formObjectScope</td>
* <td>{@link org.springframework.webflow.ScopeType#REQUEST request}</td>
* <td>The scope in which the form object will be put. If put in flow scope the
* object will be cached and reused over the life of the flow, preserving previous
* values. Request scope will cause a new fresh form object instance to be created
* each execution.</td>
* </tr>
* <tr>
* <td>errorsScope</td>
* <td>{@link org.springframework.webflow.ScopeType#REQUEST request}</td>
* <td>The scope in which the form object errors instance will be put.
* If put in flow scope the errors will be cached and reused over the life
* of the flow. Request scope will cause a new errors instance to be created
* each execution.</td>
* </tr>
* <tr>
* <td>propertyEditorRegistrar</td>
* <td>null</td>
* <td>The strategy used to register custom property editors with the data
* binder. This is an alternative to overriding the
* {@link #initBinder(RequestContext, DataBinder) initBinder} hook method. </td>
* </tr>
* <tr>
* <td>validator</td>
* <td>null</td>
* <td>The validator for this action. The validator must support the
* specified form object class. </td>
* </tr>
* <tr>
* <td>bindOnSetupForm</td>
* <td>false</td>
* <td>Set if request parameters should be bound to the form object during the
* {@link #setupForm(RequestContext) setupForm} action. </td>
* </tr>
* <tr>
* <td>validateOnBinding</td>
* <td>true</td>
* <td>Indicates if the validator should get applied when binding. </td>
* </tr>
* <tr>
* <td>messageCodesResolver</td>
* <td>null</td>
* <td>Set the strategy to use for resolving errors into message codes. </td>
* </tr>
* </table>
*
* @author Erwin Vervaet
* @author Keith Donald
*/
public class FormAction extends MultiAction implements InitializingBean {
/**
* Optional property that identifies the method that should be invoked on the
* configured validator instance, to support piecemeal wizard page validation.
*/
public static final String VALIDATOR_METHOD_PROPERTY = "validatorMethod";
/**
* The name the form object should be exposed under.
*/
private String formObjectName = "formObject";
/**
* The type of form object -- typically a instantiable class.
*/
private Class formObjectClass;
/**
* The scope in which the form object should be exposed.
*/
private ScopeType formObjectScope = ScopeType.REQUEST;
/**
* The scope in which the form object errors holder should be exposed.
*/
private ScopeType formErrorsScope = ScopeType.REQUEST;
/**
* A centralized service for property editor registration, for type conversion during
* form object data binding.
*/
private PropertyEditorRegistrar propertyEditorRegistrar;
/**
* A validator for the form's form object.
*/
private Validator validator;
/**
* Should binding from event parameters happen on form setup?
*/
private boolean bindOnSetupForm = false;
/**
* Should validation happen after data binding?
*/
private boolean validateOnBinding = true;
/**
* Strategy for resolving error message codes.
*/
private MessageCodesResolver messageCodesResolver;
/**
* A cache for dispatched action execute methods.
*/
private DispatchMethodInvoker validateMethodDispatcher = new DispatchMethodInvoker();
/**
* Return the name of the form object in the configured scope.
*/
public String getFormObjectName() {
return this.formObjectName;
}
/**
* Set the name of the form object in the configured scope. The form object
* will be included in the configured scope under this name.
*/
public void setFormObjectName(String formObjectName) {
this.formObjectName = formObjectName;
}
/**
* Return the form object class for this action.
*/
public Class getFormObjectClass() {
return this.formObjectClass;
}
/**
* Set the form object class for this action. An instance of this class will
* get populated and validated.
*/
public void setFormObjectClass(Class formObjectClass) {
this.formObjectClass = formObjectClass;
getValidateMethodDispatcher().setParameterTypes(new Class[] { this.formObjectClass, Errors.class });
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -