Spring-SimpleFormController

Spring-SimpleFormController

 
SimpleFormController在Controller继承体系位于最底端,是一个功能强大,而且比较复杂的Controller。下面是SimpleFormController的主要继承图谱:
Spring学习笔记(十二)----SimpleFormController(上)
SimpleFormController本身的强大和复杂性从上图就可以看出。实际上,SimpleController除了在处理上要经过一个多步的流程,而且在处理不同类型请求时(如post,get)。(希望大家看一下上面的图和bean定义)
下面是SimpleFormController的处理流程,分为post方法和get方法。
get方法处理流程(一般连接的形式为get方法):
  1. 按请求转到相应controller;
  2. 调用formBackingObject()方法,创建一个command对象的实例(如果设定了commandClass,则不需要override formBackingObject()方法了);
  3. 调用initBinder(),注册需要的类型转换器;
  4. 调用showForm()方法,返回一个view,即准备呈现给用户的视图,一般情况下这个方法是不需要override的,只需设定formView即可;
  5. 调用referenceData()方法。这个方法返回一个Map对象,可以把在view中需要展现的数据放入这个Map中;
  6. 转到formView指定的视图。

一般情况下步骤2,3,4的方法是不需要override的,它们只需要在配置文件中进行相关属性的定义即可。而步骤5的referenceData()一般是要在自己的SimpleFormController中进行重新定义的。

post方法处理流程(一般用于处理表单):

  1. 按请求转到相应controller;
  2. 调用formBackingObject()方法,创建一个command对象的实例(如果设定了commandClass,则不需要override formBackingObject()方法了);
  3. 把请求参数注入表单对象;
  4. 执行onBind()方法;
  5. 执行validator验证。
  6. 执行onBindAndValidate()方法;
  7. 若有err,则转到formView视图;
  8. 执行onSubmit()方法或doSubmitAction()方法。
同处理get方法一样,步骤2的formBackingObject()方法一般是不会去重载的。步骤3也不需要我们做什么。真正需要我们做的是4,5,6,8步。当然4,5,6是可选的,如果有需要,可以去实现它们中的一个或几个,执行的步骤是按上面的顺序。但步骤8的两个方法是必须选一个的(两个都override是没有意义的)。
这里要说明一下onSubmit()方法和doSubmitAction()方法。在SimpleFormController中,有三种形式的onSubmit方法,它们是:
  • onSubmit(req, res, command, errs);
  • onSubmit(command, errs);
  • onSubmit(command);

但是这三种方法不是孤立的,第一个方法在执行中会调用第二个方法,第二个方法在执行中会调用第三个方法。一般在定义自己的SimpleFormController时只是override onSubmit(command)方法。

doSubmitAction()方法是一个有意思的方法,它的完整定义如下:

protected void doSubmitAction(Object obj) throws Exception

它的返回类型是void,而不是我们预想的ModelAndView,并且也不返回任何视图层需要的数据。实际上doSubmitAction执行完毕后会自动转到successView视图。并且如果配合sessionForm来使用的的话(sessionForm设为true),那么在转到successView视图后,在session中可以取到先前的表单对象(所谓的POJO)。

说了这么多,还是看个实例比较形象(SimpleFormController(中))。

下面是一个SimpleFormController的实例,虽然有些地方显得吹毛求疵,但主要是为了表达一个完整的流程。
首先先看配置文件,web.xml就不说了,下面的是 /WEB-INF/mvc-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--InternalResourceViewResolver-->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

<!--SimpleUrlHandlerMapping-->
<bean id="urlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/user.htm">userController</prop>
</props>
</property>
</bean>
</beans>

然后是/WEB-INF/controller-config.xml:

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

<bean id="userValidator" class="com.yangsq.validator.UserValidator"/>

<bean id="userController"
class="com.yangsq.controller.UserController">
<property name="commandName">
<value>command</value>
</property>
<property name="commandClass">
<value>com.yangsq.domain.User</value>
</property>
<property name="validator">
<ref bean="userValidator" />
</property>
<property name="formView">
<value>user</value>
</property>
<property name="successView">
<value>hello</value>
</property>
</bean>
</beans>

这里需要对上面的配置文件进行说明。commandName属性的设置是为了可以在页面中使用Spring tag,对业务流程没有影响,如果不想使用Spring tag,则这个属性可以没有。commandClass指定了封装表单的类,注意,要指定完整的路径,不能只指定一个类名,也不能<ref .../>。validator的设定说明了要使用验证器。formViewsuccessView这两个属性设定了转向的页面,它们是父类所具有的,所以不需要在你的controller中再注入了。

下面是验证器:

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.yangsq.domain.User;

public class UserValidator implements Validator{

public boolean supports(Class clazz) {
return clazz.equals(User.class);
}

public void validate(Object obj, Errors err) {
User user = (User) obj;
if (user.getPhone().length() < 7) {
user.setCreateTime(null);
err.reject("phoneErr", "电话号码位数要大于7");
}else if (user.getAge() <= 0) {
user.setCreateTime(null);
err.reject("ageErr", "年龄要大于0");
}
}
}

验证器实现了Validator借口,supports和validate这两个方法是必须实现的。验证器的主要任务是对表单类进行验证,这时请求的数据已经封装到表单类里了。

下面是表单类:

public class User {
private String account;
private String phone;
private int age;
private String city;
private Date createTime;

public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
属性包括用户名称,电话,年龄,城市和创建日期。此外,为了表达一个完成的演示,还创建了一个city类:
public class City {
private String cityName;
private String cityNo;
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCityNo() {
return cityNo;
}
public void setCityNo(String cityNo) {
this.cityNo = cityNo;
}
}

下面就是这个例子的主角,UserController:

public class UserController extends SimpleFormController {

protected Map referenceData(HttpServletRequest req) throws Exception {
Map map = new HashMap();

List cityList = new ArrayList();
City city1 = new City();
city1.setCityName("BeiJing");
city1.setCityNo("010");
cityList.add(city1);
City city2 = new City();
city2.setCityName("ShangHai");
city2.setCityNo("020");
cityList.add(city2);

map.put("cityList", cityList);
return map;
}

protected void onBind(HttpServletRequest req, Object obj) throws Exception {
User user = (User) obj;
//format the infor
user.setAccount(user.getAccount().trim());
user.setPhone(user.getPhone().trim());
}


protected void onBindAndValidate(HttpServletRequest req, Object obj, BindException err) throws Exception {
User user = (User) obj;
user.setCreateTime(new Date());
}

protected ModelAndView onSubmit(Object obj) throws Exception {
User user = (User) obj;
return new ModelAndView(this.getSuccessView(), "user", user);
}
}

下面是使用到的页面 user.jsp的主要部分(表单):

<FORM action="user.htm" method="post">
<TABLE>
<TBODY>
<TR>
<TD>
<spring:bind path="command.account">
用户名:<INPUT name="${status.expression}" type="text" value="${status.value}"/>
</spring:bind>
</TD>
</TR>
<TR>
<TD>
<spring:bind path="command.age">
年龄:<INPUT name="${status.expression}" type="text" value="${status.value}"/>
</spring:bind>
</TD>
</TR>
<TR>
<TD>
<spring:bind path="command.phone">
电话号码:<INPUT name="${status.expression}" type="text" value="${status.value}"/>
</spring:bind>
</TD>
</TR>
<TR>
<TD>
<spring:bind path="command.city">
城市:
<SELECT name="${status.expression}">
<c:forEach items="${cityList}" var="city" varStatus="loopStep">
<OPTION value="${city.cityNo}" <c:if test="${city.cityNo == status.value}">selected</c:if>>
<c:out value="${city.cityName}"/>
</OPTION>
</c:forEach>
</SELECT>
</spring:bind>
</TD>
</TR>
<TR>
<TD>
<spring:bind path="command.*">
<c:out value="${status.errorMessage}"/>
</spring:bind>
</TD>
</TR>
<TR>
<TD align="center">
<INPUT type="submit" value="提交" />
</TD>
</TR>
</TBODY>
</TABLE>
</FORM>

hello.jsp的主要部分:

<TABLE>
<TBODY>
<TR>
<TD>
User infor:
</TD>
</TR>
<TR>
<TD>
<c:out value="${user.account}" />
</TD>
</TR>
<TR>
<TD>
<c:out value="${user.age}" />
</TD>
</TR>
<TR>
<TD>
<c:out value="${user.phone}" />
</TD>
</TR>
<TR>
<TD>
<c:out value="${user.city}" />
</TD>
</TR>
<TR>
<TD>
<c:out value="${user.createTime}" />
</TD>
</TR>
</TBODY>
</TABLE>

在 SimpleFormController(下)会结合效果图,说明一下整个流程:

在地址栏中直接输入地址 http://localhost:8080/SpringMVC/user.htm(SpringMVC是工程名),会显示如下页面(user.jsp):
Spring学习笔记(十二)----SimpleFormController(下)
处理原理:在地址栏输入地址提交后,实际上是对user.htm的一个get请求,在 SimpleFormController(上)里介绍了SimpleFormController对get方法的处理流程。这里会执行UserController的referenceData方法。在referenceData方法里生成了一个城市类的List。用于user.jsp(formView)的城市下来框。
在上图框体内填入信息:
Spring学习笔记(十二)----SimpleFormController(下)
点击提交,会出现如下页面:
Spring学习笔记(十二)----SimpleFormController(下)
处理原理:填入信息后点击提交,会以user.htm路径按post方法提交表单,在 SimpleFormController(上)里介绍了SimpleFormController对post方法的处理流程。在UserController里,首先会执行onBind方法,然后是UserValidator类(验证器),然后是onBindAndValidate方法。由于UserValidator类检验出了错误(电话号码位数不够),所以执行完onBindAndValidate方法后会转到user.jsp(formView)。而不会执行onSubmit方法。
修改电话号码:
Spring学习笔记(十二)----SimpleFormController(下)
点击提交:
Spring学习笔记(十二)----SimpleFormController(下)
转到hello.jsp(successView)页面。
处理原理:填入信息后点击提交,会以user.htm路径按post方法提交表单。处理流程和上述失败时差不多。只是在执行完onBindAndValidate方法后由于没有了错误,接着执行onSubmit方法。
以上是整个的处理流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值