赠人玫瑰,手留余香.人生最大的快乐不在于占有什么而在于追求什么的过程.
参考文章:http://fhd001.iteye.com/blog/1131713 http://blog.sina.com.cn/s/blog_4b8677cd0100089t.html
http://www.cnblogs.com/hibernate3-example/archive/2011/10/26/2492534.html
processFormSubmission()。showForm()这个方法已经被类SimpleFormController实现了并被限定为final,你不可以在继承SimpleFormController的子类里覆写这个类。processFormSubmission()这个方法尽管可以去覆写但由于它几乎可以满足所有的要求,因此一般也不会有人去重写它。
它的处理流程是这样的:
get请求来到时,这样处理:
1) 请求传递给一个controller对象
2) 调用formBackingObject()方法,创建一个command对象的实例。
3) 调用initBinder(),注册需要的类型转换器
4) 调用showForm()方法,返回准备呈现给用户的视图 ,如果“bindOnNewForm”属性设为true,则ServletRequestDataBinder
5) 调用referenceData()方法,准备给用户显示相关的数据。如用户登录需要选择的年度信息
6) 返回formView指定的视图
对应的活动图如下:
post请求来到时,这样处理:
1) 如果sessionForm属性没有设定,则调用formBackingObject()方法,创建一个command对象的实例。否则从session中取得表单对象
2) 将请求传来的参数写入command对象,看它的源代码,会发现它是这样来做的:
ServletRequestDataBinder
binder.bind(request);
3)执行onBind()方法,在绑定数据之后,验证数据之前对表单数据进行一些自制的修改动作。
4) 如果设置为要求验证(validateOnBinding属性被设定),则调用validator类进行数据验证
5) 调用onBindAndValidate()方法,该方法允许自定义数据绑定和校验处理
6)执行processFormSubmission()检验 Errors对象中含不含错误,如果含有错误则执行showForm()返回到填写表单页面;否则执行onSubmit()方法,进行提交表单,然后转向成功页面。
相应的活动图如下:
即:1.当表单控制器接收到GET请求时,它调用formBackingObject()方法,创建表单对象。该方法可以被子类覆盖,对于编辑操作的表单来说,你可以通过该方法从数据库中加载表单对象,当表单页面显示时,表单显示出待编辑的数据了;
2.表单对象和页面表单数据之间需要通过属性编辑器实现双向转化,对于非基本数据类型或String类型的属性来说,你可能需要注册一些自定义编辑器。你可以通过覆盖initBinder()方法,通过调用binder.registerCustomEditor()的方法注册编辑器;
3.表单对象通过bindOnNewForm属性(可以通过配置设置,默认为false)判断是否需要将GET请求参数绑定到formBackingObject()方法创建的表单对象中。如果bindOnNewForm为true,执行绑定操作,在绑定完成后,还将调用onBindOnNewForm()回调方法(子类可以提供具体实现)。否则到下一步。不过一般情况下,GET请求参数是用于加载等编辑表单对象的ID值,如topicId、forumId等,一般无需进行绑定;
4.调用referenceData()方法(子类可提供具体实现)准备一些关联的数据,如性别下拉框数据,学历下拉框数据等。一般采用ModelMap创建视图业务中需要用到的请求属性数据,键为属性名,值为属性值,如ModelMap("param1", "paramValue1");
5.使用控制器formView定义的视图渲染表单对象;
6.用户填写或更改表单后,提交表单,向表单控制器发起一个POST请求;
7.接收到POST请求时,表单控制器知道这是一个表单数据提交的操作,所以启动表单提交处理流程;
8.首先通过sessionForm属性判断表单控制器是否启用了Session。如果启用了Session,直接从Session中取出原表单对象,否则再次调用formBackingObject()方法构造出一个表单对象。sessionForm默认为false,可以通过配置进行调整,启用Session可能提高运行性能,但会占用一定的内存;
9.将POST请求参数填充到表单对象中;
10.调用onBind()方法,该方法允许你在表单填充完成后,合法性校验之前执行一些特定的操作;
11.如果validateOnBinding属性设置为true,注册在控制器中的校验器开始工作,对表单对象的属性值执行合法性校验。如果有合法性错误,将被注册到Errors对象中(关于如何注册校验器,我们将稍后介绍);
12.调用onBindAndValidate()方法,该方法允许你在数据绑定及合法性校验后,执行一些额外的自定义操作,你也可以在这里,执行一些额外的合法性校验;
13.调用processFormSubmission()方法处理提交任务,该方法内部又包含后续几步工作;
14.判断方法入参传入errors是否包含错误,如果包含错误返回到formView对应的表单页面中,否则到下一步;
15.通过isFormChangeRequest()方法(默认为false)判断请求是否为表单更改请求,如果为true,调用onFormChange()方法,然后返回到formView对应的表单页面,否则到下一步;
16.如果子类覆盖了onSubmit()方法,执行之,否则执行子类的doSubmitAction()方法。通过这两者之一完成业务的处理,然后返回successView属性指定的成功页面。
在web应用程序中,经常要处理表单。表单控制器既要为用户显示表单,又要处理表单提交。表单处理可能是件非常复杂且充满变数的任务。如果从头开始构建表单控制器,会牵涉到太多的表单处理细节。
Spring MVC提供的SimpleFormController类定义了基本的表单处理流程。它支持命令对象的概念,并且可以将表单域的值绑定到命令对象的同名属性上。通过扩展SimpleFormController类,控制器就可以继承处理表单的能力。
当HTTP GET请求SimpleFormController显示表单时, 它会将表单视图呈现给用户。当HTTP POST请求提交该表单时,SimpleFormController就会将表单域的值绑定到命令对象上,并调用onSubmit()方法,以这种方式来处理表单提交。如果表单处理成功,会呈现success视图。否则,呈现带错误信息的表单视图。
为了适应不同的表单需求,SimpleFormController允许你覆盖其中的生命周期方法,以此定制表单处理流程。
创建表单控制器
通过扩展SimpleFormController类,可以为这个控制器指定命令类(在本例中是Reservation),然后表单域的值将被绑定到命令对象的同名属性中。你还可以指定视图要访问的命令对象的名称(在本例中是reservation),但这是可选的,默认名称为command。
package com.sunshine.controller;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.validation.BindException;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import com.sunshine.pojo.UserModel;
public class RegisterController extends SimpleFormController {
/**
* 可以通过依赖注入 注入命令实现类 ,也可以在类的构造方法中使用父类的方法 setCommandClass(Class) public
* LoginCommandController(){ setCommandClass(UserModel.class); }
*/
@Override
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
UserModel user = new UserModel();
user.setName("请输入用户名");
return user;
}
@Override
protected Map<String, Object> referenceData(HttpServletRequest request)
throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("cityList", Arrays.asList("山东", "北京", "上海"));
return map;
}
/*
* @Override protected void doSubmitAction(Object command) throws Exception
* { UserModel userModel = (UserModel) command;
* System.out.println(userModel); }
*/
@Override
protected ModelAndView onSubmit(Object command, BindException errors)
throws Exception {
System.out.println("RegisterController.onSubmit()");
UserModel userModel = (UserModel) command;
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName(getSuccessView());
modelAndView.addObject("user", userModel);
return modelAndView;
}
protected void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
System.out.println("RegisterController.initBinder()");
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
// binder.registerCustomEditor(Date.class,"birthday", new DateEditor());
}
/*@Override
protected void onBind(HttpServletRequest request, Object command)
throws Exception {
// TODO Auto-generated method stub
super.onBind(request, command);
System.out.println("RegisterController.onBind()");
}*/
}
由于表单域的值都是作为字符串提交的,为了将这些绑定到命令对象上,表单控制器可能需要进行类型转换。类型转换实际是由注册在这个控制器中的属性编辑器来执行的。Spring预先注册了几个属性编辑器,用来转换大家熟知的数据类型,如数字类型和布尔类型。你必须为其他的数据类型注册定制的编辑器,,如java.util.Date。定制的属性编辑器都被注册到initBinder()方法的ServletRequestDataBinder参数中。在绑定表单域值时如果发生任何错误,SimpleFormController会自动呈现带有错误信息的表单视图。如果一切顺利,则会调用onSubmit()方法来处理表单提交。在onSubmit()方法中,可以通过参数获取绑定了表单域值的命令对象。对于这个控制器而言,它是个Reservation对象,因为你将命令类设置为Reservation。onSubmit()方法的三个变体可供覆盖。为了访问符合你需要的方法参数,应该覆盖它们中最简单的那个。
protected ModelAndView onSubmit(Object command)throws Exception;
protected ModelAndView onSubmit(Object command,BindException errors)
throws Exception;
protected ModelAndView onSubmit(HttpServletRequest request,HttpServletResponse response,
Object command,BindException errors)throws Exception;
在覆盖onSubmit()方法时,必须返回ModelAndView对象。如果只需在命令对象上执行一个动作,并在这个动作完成时返回成功视图,可以覆盖doSubmitAction()方法,它的返回类型为void,默认呈现成功视图。
在声明这个控制器时,为了进行预订,还需要一个指向服务层中的reservationService Bean的引用。此外,还必须为这个表单控制器设置表单视图和成功视图,分别是formView和successView属性。
<bean id="registerController" class="com.sunshine.controller.RegisterController">
<!-- 可以通过依赖注入 注入命令实现类 ,也可以在类的构造方法中使用父类的方法setCommandClass(Class)-->
<property name="commandClass" value="com.sunshine.pojo.UserModel"/>
<property name="commandName" value="user"></property>
<property name="formView" value="register"></property>
<property name="successView" value="success"></property>
</bean>