springmvc4.0 控制器获取表单控制器action 通过bean接收 参数需要相同数量吗

Spring MVC的表单控制器——SimpleFormController
我的图书馆
Spring MVC的表单控制器——SimpleFormController
  大多数Web应用都会遇到需要填写表单的页面,当表单提交成功后,表单的数据被传送给Web服务器中处理。处理成功后导向到一个成功页面,如果
操作失败则导向到一个错误报告页面。此外,在表单数据处理之前还会进行表单数据的验证,保证客户端提交的表单数据是合法有效的,如果数据不合法,请求返回
到原表单页面中,以便用户根据错误信息进行修改。
  假设你想成为论坛的用户时,必须填写一张用户注册表单,这可能包括用户名、密码、Email等注册信息。用户提交表单后,服务器验证注册数据合法性,如果你填写的信息是合法的,系统将在数据库中创建一个新用户,用户注册就完成了。
  用户注册表单控制器
  通过扩展SimpleFormController可以按照标准的表单处理流程处理用户注册的请求,UserRegisterController用于负责处理用户注册的请求:
  代码清单 1 UserRegisterController
package com.baobaotao.web.
import org.springframework.web.servlet.mvc.SimpleFormC
import com.baobaotao.domain.U
import com.baobaotao.service.BbtF
public class UserRegisterController extends SimpleFormController {
private BbtForum bbtF
public UserRegisterController(){
setCommandClass(User.class); ①指定命令对象(这时也称表单对象)的类型
public void setBbtForum(BbtForum bbtForum) {
this.bbtForum = bbtF
②通过该方法处理表单提交请求
protected void doSubmitAction(Object command) throws Exception {
User user = (User)
bbtForum.registerUser(user);
}  在①处指定表单对象的类型,以便控制器自动将表单数据绑定到表单对象中,你也可以直接在配置文件中通过commandClass属性进行设置:
  &property name="commandClass" value=" com.baobaotao.domain.User"/&
  在②处复写了doSubmitAction()方法,在该方法内部通过调用业务层的bbtForum保存表单对象,创建新用户。当你不需要返回
模型对象给成功页面时,复写doSubmitAction()方法是最佳的选择,因为该方法没有返回值。如果需要返回模型对象给成功页面,那么就必须复写
表单控制器的onSubmit
()方法。用户注册成功后,我们一般需要在成功页面中根据用户信息提供个性化的内容,这就要求控制器返回相应的User模型对象,此时需要在
UserRegisterController中复写onSubmit
protected ModelAndView onSubmit (Object command, BindException errors)
throws Exception {
User user = (User)
bbtForum.registerUser(user);
return new ModelAndView(getSuccessView(), "user", user);①user中包含注册用户的信息
  当你复写onSubmit ()方法后,doSubmitAction()方法就不会得到执行了, onSubmit
()方法比doSubmitAction()方法具有更高的调用优先级,所以你只要根据要求复写两者中的一个方法就可以了。在onSubmit
()中返回的ModelAndView的逻辑视图名应该是通过表单控制器的successView属性指定而不应该硬编码,所以在①处我们通过
getSuccessView()获取这个配置值。
  表单控制器的工作流程从表单页面提交开始,处理成功后转向成功页面,这个流程涉及到两个视图:表单页面和成功页面,这需要在表单控制器中通过属性进行定义:
&bean name="/registerUser.html" class="com.baobaotao.web.user.UserRegisterController"&
&property name="bbtForum" ref="bbtForum" /&
&property name="formView" value="register" /& ①表单录入页面(逻辑视图名,下同)
&property name="successView" value="registerSuccess" /& ②成功页面
  通过formView属性指定表单录入页面对应的逻辑视图名,而successView属性表示成功页面的视图逻辑名。通过代码清单
2前后缀视图解析器的处理,它们将分别对应WEB-INF/jsp/register.jsp和WEB-INF/jsp
/registerSuccess.jsp的JSP页面。
  代码清单 2 前后缀视图解析器
&bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"&
&property name="prefix"& ①前缀
&value&/WEB-INF/jsp/&/value&
&/property&
&property name="suffix"& ②后缀
&value&.jsp&/value&
&/property&
  一般情况下表单录入页面需要通过Spring表单标签绑定表单对象,以便根据表单对象初始值生成表单页面,在校验失败后能够重现提交前的表单数据。让我们看看这个register.jsp用户注册页面的内容:
  代码清单 3 register.jsp:用户注册页面
&%@ page language="java" contentType="text/ charset=UTF-8" pageEncoding="UTF-8"%&
&%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %&
&title&宝宝淘论坛用户注册&/title&
①Spring MVC表单标签,可以直接和/registerUser.html控制器
绑定(fromView),无需通过action指定提交的目标地址。
&form:form&
用户名:&form:input path="userName" /&
密 码:&form:password path="password" /&
Email:&form:input path="email" /&
&input type="submit" value="注册" /&
&input type="reset" value="重置" /&
&/form:form&
  在①处,应用Spring的表单标签定义了一个能够和表单对象绑定的页面表单。和Struts不同的是作为表单标签
的&form:form&元素无需设定提交地址(在Struts中必须指定表单标签的action属性),Spring
MVC能够自动根据控制器的formView属性获知该表单页面的提交地址。使用过Struts
Action开发表单提交功能的读者也许会知道开发Struts处理表单功能是比较麻烦,因为可能会为了开发一个表单设计多个Action:一个用户初始
化表单,另一个用于提交表单。而Spring的SimpleFormController的高明之处在于,它已经将表单处理工作流程编制到控制器中,我们
仅需要在子类中复写开放出方法就可以充分享受预定义工作流程的好处。我们应该如何有选择地覆盖父类方法,以便正确地影响表单工作流程呢?这回答这个问题需
要对SimpleFormController的工作流程有一个详细的了解。
  表单控制器完整工作流程
  使用SimpleFormController时,你无需为初始化表单编写额外的控制器,当你通过GET请求访问表单控制器时,表单控制器自动
将请求导向到表单录入页面。而当你通过POST请求访问表单控制器时,表单控制器执行表单提交的业务,根据处理成功与否,或导向到成功页面,或导向到表单
录入页面(当发生异常时导向到错误页面)。
  SimpleFormController的工作流程比较复杂,我们通过下面的流程图对此进行描述:
  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属性指定的成功页面。
  我们可以按照以上表单控制器的工作流程,根据业务需要有选择地覆盖一些父类的方法完成特定的操作。假设我们在开发一个编辑用户信息的功能,在展
现表单前需要先从数据库中查询出用户信息并在更改表单中展现,这时,我们仅需覆盖formBackingObject()方法,执行查询操作就可以了,其
代码形如下所示:
  ① 根据请求参数从数据库中查询出User对象,作为更新用户表单的初始值
protected Object formBackingObject(HttpServletRequest request) throws Exception {
int userId = ServletRequestUtils.getIntParameter(request, "userId",-1);
User user = bbtForum.getUser(userId);
user.setUserName("user1");
  ServletRequestUtils是Spring 2.0新增的工具类,可以方便地按类型获取请求参数的值,它位于org.springframework.web.bind包中。
  表单数据校验
  当UserRegisterController调用BbtForum#registerUser()方法注册用户时,确保User对象数据的合法性是非常重要的,你不希望用户的Email地址是非法的,用户名不应和已经用户名相同。
  org.springframework.validation.Validator接口为Spring
MVC提供了数据合法性校验功能,该接口有两个方法,说明如下: boolean supports(Class
clazz):判断校验器是否支持指定的目标对象,每一个校验器负责对一个表单类的对象进行检验;
  void validate(Object target, Errors errors):对target对象进行合法性校验,通过Errors返回校验错误的结果。
  下面,我们编写一个负责对User对象进行数据合法性校验的校验器,请看以下的代码:
  代码清单 4 UserValidator:校验User对象值合法性
package com.baobaotao.domain.UserV
import java.util.regex.M
import java.util.regex.P
import org.springframework.validation.E
import org.springframework.validation.ValidationU
import org.springframework.validation.V
public class UserValidator implements Validator {
private static final Pattern EMAIL_PATTERN = Pattern ①合法Email正则表达式
.compile("(?:w[-._w]*w@w[-._w]*w.w{2,3}$)");
public boolean supports(Class clazz) { ②该校验器支持的目标类
return clazz.equals(User.class);
public void validate(Object target, Errors errors) { ③对目标类对象进行校验,错误记录在errors中
User user = (User) ③-1 造型为User对象
③-2 通过Spring提供的校验工具类进行简单的规则校验
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName",
"required.username", "用户名必须填写");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password",
"required.password", "密码不能为空");
validateEmail(user.getEmail(), errors); ③-3 校验Email格式
private void validateEmail(String email, Errors errors) {④Email合法性校验
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email",
"required.email", "Email不能为空");
Matcher m = EMAIL_PATTERN.matcher(email); ④-1 通过正则表达式校验Email格式
if (!m.matches()) {
errors.rejectValue("email", "invalid.email", "Email格式非法");
  在②处,我们声明该校验器支持的表单对象为User类,如果错误地将UserValidator用于其它对象校验,Spring MVC就会根据supports()方法驳回操作。
  对于一般的空值校验来说,直接使用Spring提供的ValidationUtils校验工具类是最简单的办法(如③-2所示)。
ValidationUtils的rejectIfEmptyOrWhitespace()、rejectIfEmpty()以及Errors的
reject()、rejectValue()方法都拥有多个用于描述错误的入参,通过下图进行说明:
  1)对应字段:表示该错误是对应表单对象的哪一个字段,Spring MVC的错误标签可以通过path属性访问该字段错误消息;
  2)错误代码:表示该错误对应资源文件中的键名,Spring MVC的错误标签可以据此获取资源文件中的对应消息。如果希望实现错误消息的国际化,你就必须通过错误代码指定错误消息;
  3)默认消息:当资源文件没有对应的错误代码时,使用默认消息作为错误消息。
  我们“惊讶地”发现入参列表并没有包括需要校验的目标表单对象,那如何对目标表单对象实施校验呢?原来目标对象已经包含在errors对象中,在校验方法内部会从errors中取得目标方法并施加校验。
  在④处,我们通过正则表达式对Email格式进行校验。我们直接使用JDK 1.4
java.util.regex包中提供的正则表达式工具类完成校验的工作。由于Email模式是固定的,为了提高性能,我们在①处用final
static的方式定义了一个Email合法模式的Pattern对象。
  编写好UserValidator,我们需要将其装配到UserRegisterController控制器中,其配置如下所示:
&bean name="/registerUser.html" class="com.baobaotao.web.user.UserRegisterController"&
&property name="bbtForum" ref="bbtForum" /&
&property name="formView" value="register" /&
&property name="successView" value="registerSuccess" /&
&property name="validator"& ①装配校验器
&bean class="com.baobaotao.domain.UserValidator" /&
&/property&
  在①处我们通过validator指定了一个对User表单对象进行校验的校验器,如果你有多个校验器类(很少见),可以通过validators属性进行指定。
  我们通过UserValidator可以很好地完成User对象属性值的格式检查,可是仔细想想是否还存在遗漏呢?也许你已经指
出:userName不能和数据库中已有用户名重复!你当然可以在UserValidator中通过注入业务对象完成userName重复性的校验,但对
于这种需要通过业务对象完成的校验操作,一种更好的方法是通过覆盖控制器的onBindAndValidate()方法,直接在控制器中提供检验。这带来
了一个好处,UserValidator无需和业务对象打交道,而UserRegisterController本身已经拥有了业务对象的引用,所以调用
业务对象执行校验非常方便。下面的代码展示了UserRegisterController中onBindAndValidate()的内容:
  代码清单 5 UserRegisterController#onBindAndValidate()通过业务对象完成校验
package com.baobaotao.web.
public class UserRegisterController extends SimpleFormController {
protected void onBindAndValidate(HttpServletRequest request,
Object command, BindException errors) throws Exception {
User user = (User)
if (bbtForum.isExsitUserName(user.getUserName())) {①通过业务对象完成检验
errors.rejectValue("userName", "exists.userName", "用户名已经存在");
  我们在UserRegisterController覆盖了父类的onBindAndValidate()方法,通过BbtForum业务对象的方法判断userName是否已经被占用,如果已经被占用,将相应错误添加到errors对象中。
  通过错误标签显示错误
  当存在合法性检查错误时,请求被导向到formView的表单页面中。但是如果register.jsp表单页面没有做任何配合操作,校验错误
的信息就象空气和电磁波一样,虽然存在但却看不到,如果我们在register.jsp中相应地添加一些Spring错误标签这面魔法镜,错误信息就现形
了。下面我们对register.jsp视图文件进行调整,加入显示校验错误的标签:
  代码清单 6 register.jsp:添加错误标签
&form:form&
用户名:&form:input path="userName" /&
&font color="red"&&form:errors path="userName" /&&/font&①userName的校验错误
密 码:&form:password path="password" /&
&font color="red"&&form:errors path="password" /&&/font&②password的校验错误
Email:&form:input path="email" /&
&font color="red"&&form:errors path="email" /&&/font& ③email的校验错误
&input type="submit" value="注册" /&
&input type="reset" value="重置" /&
&/form:form&
  由于我们在构造错误时,使用了错误代码,错误代码是引用国际化资源的凭借。为了让错误代码生效,我们就必须提供相应的国际化资源。假设我们将错
误资源放在基名为errors的国际化资源文件中,提供诸如errors.properties和errors_zh_CN.properties的国际
化资源文件,那么错误信息就可以做到国际化了。以下是errors.properties资源文件的内容(绿色部分为错误代码):
required.username=user name can't be empty.
required.password=password can't be empty.
required.email=email can't be empty.
invalid.email=email is valid.
exists.userName=user name already existed.
  将诸如errors.properties和errors_zh_CN.properties的整套资源文件都放到类路径下后,还需要在上下文
中引用这些国际化资源。因为国际化资源信息仅需要在Web展现层使用,所以直接在DispatcherServlet上下文对应的baobaotao-
servlet.xml配置文件中声明就可以了:
  代码清单 7 baobaotao-servlet.xml
&bean id="messageSource" ① 注意一定要使用“messageSource”这个Bean名称
class="org.springframework.context.support.ResourceBundleMessageSource"&
&property name="basenames"&
&value&errors&/value& ② 指定资源文件基名称
&/property&
  通过以上的配置后,故意填写一个错误的注册信息,在提交表单后你将看到如下形如以下的错误提示页面:
  虽然Spring MVC允许你使用不同类型的处理器,但绝大多数情况下我们使用控制器(Controller)处理请求。Spring
MVC为不同需求提供了多种类型的控制器,控制器一般拥有一个特定用途的工作流程,如表单控制器编制了表单处理通用工作流程,你仅需要实现
SimpleFormController特定方法,并配置使用Spring表单标签就可以轻松完成表单功能的开发了。Spring MVC如何防止重复提交?类似Struts Token机制!
首先,需要将继承了SimpleFormController之类的sessionForm设为true。这样,在显示一个新表单时,Spring
会将command存放在session中,而在提交表单时,Spring会从session中取出此command,随后立即从session中删除存
放command的attribute。如果发现在session中没有command,Spring将其断定为重复提交,转而执行
handleInvalidSubmit(request,response),可覆盖此方法负责防止重复提交的任务。可以这么说,当
setSessionForm(true)之后,如果没有先后经历显示表单、提交表单的过程,就会被认为是重复提交表单。而有一些情况下却必须重复提交表
单,如,修改数据库的数据后,试图写入数据库时因某些异常失败,如果此时异常被当前页面捕获并依旧返回当前页面,由于command已经被Spring在
后台从session中移走,因此,就被认为是无效重复提交,从而导致第二次经修改后的记录无法正确提交到数据库中。
handleInvalidSubmit()必须考虑到这种情况。
TA的最新馆藏【Spring】【笔记】《Spring In Action》第7章 Spring MVC入门
7.1 SpringMVC 起步
7.1.1 SpringMVC的生命周期
发出一个request。
首先到达Spring的DispatcherServlet,SpringMVC所有的请求都会通过一个前端控制器Servlet。在这里一个单实例的Servlet将请求委托给应用程序的其他来执行实际的处理。在SpringMVC中,DispatcherServlet就是前端控制器。
DispatcherServlet将请求发送给SpringMVC控制器。DispatcherServlet会查询一个或多个处理器映射来确定请求发向哪里,处理器映射会根据请求所携带的URL信息来进行决策。
控制器对request进行处理
控制器在完成逻辑处理后,通常会产生一些信息,这些信息称为模型(model)。信息需要发送给一个视图(View)
控制器将模型数据打包,并且标示出用于渲染输出的视图名称。接下来会将请求连同模型和视图名称发送回DispatcherServlet。这里发送的仅仅是一个逻辑名,并不是某个具体的页面。
DispatcherServlet将会使用视图解析器来将逻辑视图名匹配为一个特定的视图实现。
数据返回视图,交付模型数据,视图将使用模型数据渲染输出,并通过这个输出将响应对象传递给客户端。
7.1.2 搭建SpringMVC
SpringMVC的核心是DispatcherServlet,与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml文件中进行配置。
& servlet-class& org.springframework.web.servlet.DispatcherServlet
属性:DispatcherServlet在加载时会从一个基于这个Servlet名字的XML文件中加载Spring应用上下文。在这个例子中,DispatcherServlet将尝试从一个名为spitter-servlet.xml的文件来加载应用上下文。
通过将DispatcherServlet映射到/,声明了它会作为默认的servlet并且会处理所有的请求,包括对静态资源的请求。
为了避免DispatcherServlet处理静态资源,Spring的m命名空间提供了元素,它会处理静态资源的请求。
spitter-servlet.xml
& beans xmln= &http://www.springframework.org/schema/beans&
xmlns:xsi= &http:..www.w3.org/2001/XMLSchema-instance&
xmlns:mvc= &httpL..www.springframework.org/schema/mvc&
xsi:schemaLocation =&httpL://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd& &
*从Spring3.0.4开始支持
建立了一个服务于静态资源的处理器。属性mapping设置成/resources/**表明路径必须以/resources开始,包括它的任意子路径。location表明要提供服务的文件位置。例子中就代表着:所有以/resources路径开头的请求都会自动由应用程序根目录下的/resources目录提供服务。因此,所有图片、样式表、script及其他静态资源都必须放在应用程序的/resources目录下。
7.2 编写基本的控制器
7.2.1 配置注解驱动的Spring MVC
DispatcherServlet需要咨询一个或多个处理器映射来明确的将请求分发给哪个控制器。Spring自带了多个处理器映射实现:
BeanNameUrlHandlerMapping:根据控制器Bean的名字将控制器映射到URLControllerBeanNameHandlerMapping:根据控制器Bean的名字将控制器映射到URL。使用该处理器映射实现,Bean的名字不需要遵循URL的约定ControllerClassNameHandlerMapping:通过使用控制器的类名作为URL基础将控制器映射到URLDefaultAnnotationHandlerMapping:将请求映射给使用@RequestMapping注解的控制器和控制方法SimpleUrlHandlerMapping:使用定义在Spring应用上下文的属性集合将控制器映射到URL
使用这些处理器映射只需在Spring中配置一个Bean,如果没有找到处理器映射Bean,DispatcherServlet将创建并使用BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping.
使用基于注解的控制器类,DefaultAnnotationHandlerMapping就可以满足需求。DefaultAnnotationHandlerMapping将请求映射到使用@RequestMapping注解的方法。但是实现注解驱动的SpringMVC并不是仅仅将请求映射到方法上,在构建控制器的时候,还需要使用注解将请求参数绑定到控制的方法参数上进行校验及信息转换,因此需要在配置文件中加入SpringMVC所提供的注解驱动特性:
这个标签注册包括JSR-303校验支持、信息转换、域格式化的支持等特性。
7.2.2 定义首页的控制器
HomeController是一个基本的SpringMVC控制器,它用于处理首页的请求
@Controller //声明为控制器
public class HomeController {
public static final int DEFAULT_SPITTLES_PER_PAGE = 25;
private SpitterService spitterS
@Inject //注入Service
public HomeController(SpitterService spitterService) {
this.spitterService = spitterS
@RequestMapping({&/&,&/homg&}) //处理对首页的请求
public String showHomePage(Map model) {
model.put(&spittles&,spitterService,getRecentSpittles(DEFAULT_SPITTELS_PER_PAGE)); //将Spittle放入模型中
return &home&; //返回视图名称
@Controller时@Component注解的一种具体化,将查找使用@Controller注解的类并将其注册为Bean。这意味着要在spring的配置文件中配置一个
在编写的构造器中使用Service作为参数并使用@Inject注解,这样当控制器初始化时Service会自动注入进来
@RequestMapping注解的作用:表明showHomePage()是一个请求处理方法,并指明了要处理&/&&/home&路径的请求。
请求处理方法的参数是一个KEY为String VALUE为Object的Map,这个Map就是模型。这里可以将HttpServletRequest、HttpServletResponse、String或者数字参数传递进来。
请求处理方法最后返回一个String类型值,这个值是要渲染结果的逻辑视图的名字。
7.2.3 解析视图
DispatcherServlet会查找一个视图解析器来将将控制器返回的逻辑视图名转换成渲染结果的实际视图。
实际上,视图解析器的工作是将逻辑视图的名字与org.springframework.web.servlet.View的实现相匹配。
Spring自带了的视图解析器实现
视图解析器
BeanNameViewResolver
查找ID与逻辑视图名称相同View的实现
ContentNegotiatingViewResolver
委托给一个或多个视图解析器,而选择哪一个取决于请求的内容类型
FreeMarkerViewResolver
查找一个基于FreeMarker的模板,它的路径根据加完前缀和后缀的逻辑视图名称来确定
InternalResourceViewResolver
在Web应用程序的WAR文件中查找视图模板。视图模板的路径根据加完前缀和后缀的逻辑视图名称来确定
JerReportsViewResolver
根据加完前缀和后缀的逻辑视图名称来查找一个Jasper Report报表文件
ResourceBundleViewResolver
根据属性文件(properties file)来查找View实现
TilesViewResolver
查找通过Tiles模板定义的视图,模板的名字与逻辑视图名称相同
URLBasedViewResolver
这是一些其他视图解析器(InternalResourceViewResolver)的积累,可以单独使用,但并没有子类强大
VelocityLayoutViewResolver
VelocityViewResolver的子类,支持通过Spring的VelocityLayoutView(模仿VelocityVelocityLayoutServlet的View实现)来进行页面的组合
VelocityViewResolver
解析基于Velocity的视图,Velocity模板的路径根据加完前缀和后缀的逻辑视图名来确定
XmlViewResolver
查找在XML文件(/WEB-INF/views.xml)中声明的View实现。这个视图解析器与BeanNameViewResolver类似,但在这里视图的声明与应用程序Spring上下文的其他是分开的
XsltViewResolver
解析基于XSLT的视图,XSLT样式表的路径根据加完前缀和后缀的逻辑视图名称来确定
解析内部视图
InternalResourceViewResolver是一个面向约定的元素(约定优于配置)。它将逻辑视图名称解析为View对象,而该对象将渲染的任务委托给Web应用程序上下文的一个模板(通常是JSP)。
/WEB-INF/views/ home .
前缀 逻辑视图名 后缀
前后缀配置方式
DispatcherServlet要求InternalResourceViewResolver解析视图的时候,它将获取一个逻辑视图名称,添加相应的前缀后缀,得到一个渲染输出的JSP路径。在内部,InternalResourceViewResolver会将这个路径传递给View对象,View对象将请求传递(dispatch)给JSP。
默认情况下,InternalResourceViewResolver创建的View对象是InternalResourceView的示例,它只会简单的将请求传递给要渲染的JSP。本例中,JSP使用了一些JSTL标签,因此需要通过设置viewClass属性来将InternalResourceViewResolver替换为JstlView。
解析Tiles视图
Apache Tiles是一个模板框架,将页面分成片段并在运行时组装成完整的页面。最初是Struts框架的一部分。
要在SpringMVC中使用Tiles,首先要在spring配置文件中将TilesViewResolver注册为一个
这个Bean声明会简历一个视图解析器,她会查找逻辑视图名称与Tiles定义名称相同的Tiles模板定义,并将其作为视图。
TilesViewResolver本身并了解Tiles定义的信息,而是依靠TilesConfigurer来记录这个信息,所以需要在spring配置文件中添加TilesConfigurer类型的Bean
/WEB-INF/views/**/views.xml
TilesConfigurer会加载一个或多个的Tiles定义,并使得TilesViewResolver可以通过它来解析视图。ANT风格的**代表在/WEB-INF/views下的所有目录查找名为views.xml的文件
TilesViewResolver在解析逻辑视图名称时,将会找到对应的模板。DispatcherServlet将会把请求传递给Tiles,并用home定义来渲染结果。
7.2.4 定义首页的视图
7.2.5 完成Spring的应用上下文
ContextLoaderListener是一个Servlet监听器,除了DispatcherServlet创建的应用上下文以外,它能够加载其他的配置文件到一个Spring应用上下文中。为了使用ContextLoaderListener,需要在web.xml文件中添加如下的声明
org.springframework.web.context.ContextLoaderListener
如果没有配置,上下文加载器会查找/WEB-INF/applicationContext.xml这个Spring配置文件。
为了给ContextLoaderListener指定一个或多个Spring配置文件,需要在servlet上下文中配置contextConfigLocation参数
contextConfiglocation
/WEB-INF/spitter-security.xml
classpath:service-context.xml
classpath:persistence-context.xml
classpath:dataSource-context.xml
contextConfigLocation参数指定了一个路径的列表。除非特别声明,路径是相对于应用程序根目录的。对其中的一些添加classpath:前缀,可以使它们能够以资源的方式在应用程序中的类路径中加载,而其他文件则添加了Web应用程序的本地路径。
7.3 处理控制器的输入
7.3.1 编写处理输入的控制器
@Controller
@RequestMapping(&/spitter&)
public class SpitterController {
... // 注入Service
@RequestMapping(value=&/spittles, method=GET)
public String listSpittlesForSpitter(@RequestParam(&spitter&) String username, Model model) {
Spitter spitter = spitterService.getSpitter(username);
model.addAttribute(spitter);
model.addAttribute(spitterService.getSpittlesForSpitter(username));
return &spittles/list&;
类级别的@RequestMapping定义了这个控制器所处理的根URL路径。最终会在SpitterController中添加多个处理方法,每个方法处理不同的请求。这里的@RequestMapping已经表明所有的请求的路径都必须以 /spitters开头。
方法级别的@RequestMappings限制了类级别所定义的@RequestMapping路径匹配。
在这里,SpitterController在类级别匹配到/spitters,在方法级别匹配到/spittles,合并起来意味着listSpittlesForSpitter()将处理/spitters/spittles的请求。method属性被设置为GET代表这个方法只会处理HTTP GET请求。
参数username使用RequestParam(&spitter&)注解表名它的值应该根据请求中名为spitter的查询参数来获取。
@RequestParam不是必须的,基于约定,如果处理方法的所有参数没有使用注解的话,将绑定到同名的查询参数上。但在查询参数与方法参数名字不匹配的时候,这个注解是有用的。最好一直使用@RequestParam而不是过于依赖约定。
7.3.2 渲染视图
7.4 处理表单
7.4.1 展现注册表单
当展现表单的时候,需要将一个Spitter对象绑定到表单域上。因为这是新建的Spitter,所以新构建的且没有初始化的Spitter是最好的。
@RequestMapping(method=RqeustMethod.GET, params=&new&)
public String createSpitterProfile(Model model) {
model.addAttribute(new Spitter());
return &spitters/edit&;
方法级别的@RequestMapping如果没有指定路径,那么这个方法将会处理类级别@RequestMapping注解所指定的路径。
@RequestMapping声明了这个方法只处理HTTP GET请求。此外,将params属性设置为new,意味着这个方法只处理对/sppiters的HTTP GET请求并要求请求中必须包含名为new的查询参数
http://localhost:8080/Spitter/spitters ?new
Servlet上下文 由SpitterController来处理 &new&查询参数
定义表单视图
&%@ taglib prefix=&sf& uri=&http://www.springframework.org/tags/form&%&
这个JSP文件使用了Spring的表单绑定库。标签将createSpitterProfile()方法所放入模型的Spitter对象绑定到表单中的各个输入域。
各输入域的path属性引用的是锁绑定的Spitter对象的属性。表单提交时,这些输入域中的值会放入Spitter对象中并提交到服务器进行处理。
在没有指明URL的情况下,将被提交到展现表单的URL路径(/spitters)
7.4.2 处理表单输入
@RequestMapping(method=RequestMethod.POST)
public String addSpitterFromForm(@Valid Spitter spitter,BindingResult bindingResult) {
if(bindingResult.hasErrors()) { // 表单校验
return &spitters/edit&;
spitterService.saveSpitter(spitter); // 保存Spitter
return &redirect:/spitters/& + spitter.getUsername(); // 重定向
返回了重定向的视图而不是指明逻辑视图名称。前缀redirect:说明请求将被重定向到指定路径。如果用户点击了浏览器的刷新按钮,通过重定向到另一个页面能够避免表单的重复提交。
对于重定向的路径,将采用 /spitters/{username}这样的形式,其中{user-name}是刚刚提交的Spitter对象的用户名
处理带有路径变量的请求
响应 /spitter/{username}请求的方法
@RequestMapping(value=&/{username}&, method=RequestMethod.GET)
public String showSpitterProfile(@PathVariable String username, Model model) {
model.addAttribute(spitterService.getSpitter(username));
return &spitters/view&;
路径中{username}是一个占位符,它对应了使用@PathVariable注解的username方法参数。请求路径中的该位置的值将作为username的值传递进去。例如请求路径是/username/habuma,那么habuma将会作为username的值传递到showSpitterProfile()中。
7.4.3 校验输入
@Valid注解用来防止错误表单输入,实际上是JavaBean校验规范的一部分(JSR-303)。
如果校验出错,校验错误将会作为第二个参数以BindingResult的形式传递给addSpitterFromForm()。
定义校验规则
JSR-303定义了一些注解,这些注解可以放到属性上来指定校验规则。
@Size(min=3, max=20, message=&Username must be between 3 and 20 characters long.&)
@Pattern(regexp=&^[a-zA-Z0-9]+$&,message=&Username must be alphanumeric with no spaces&)
展现校验错误
为用户展现错误的方式是通过BindingResult的geiFieldError()方法来获取字段的错误。但更好的方式是使用Spring的表单绑定JSP标签库来展现错误。能够渲染字段的校验错误。
&%@ taglib prefix=&sf& uri=&http://www.springframework.org/tags/form&%&
Full name:
可以为设置delimiter属性来指定多个错误的分割方式
7.5 处理文件上传
7.5.1 在表单上添加文件上传域
典型的提交会带有一个application/x-www-form-urlencoded这样的内容类型并将表单上的名称-值以&符号分割
当提交带有文件的表单时,要选择的内容类型是multipart/form-data,需要设置的enctype属性
7.5.2 接收上传的文件
@RequestMapping(method=RequestMethod.POST)
public String addSpitterFromForm(@Valid Spitter spitter, BindingResult bindingResult,
@RequestParam(value=&image&, required=false, MultipartFile image) {
if(!image.isEmpty()) {
validateImage(iamge);
saveImage(spitter.getId() + &.jpg&, image);
} catch (ImageUploadException e) {
bindingResult.reject(e.getMessage());
return &spitters/edit&;
return &redirect:/spitters/& + spitter.getUsername();
private void validateImage(MultipartFile image) {
if(!image.getContentType().equals(&image/jpeg&)) {
throw new ImageUploadException(&Only JPG images accepted&);
将文件保存到文件中
private void saveImage(String filename, MultipartFile image) throws ImageUploadException {
File file = new File(webRootPath + &/resources/& +
FileUtils.writeByeArrayToFile(file, image.getBytes());
} catch (IOException e) {
throw new ImageUploadException(&Unable to save image&, e);
File对象准备就绪,就可以使用ApacheCommonsIO的FileUtils将图片数据写入文件中。
还可以将文件保存到云中&&
7.5.3 配置Spring支持文件上传
DispatcherServlet本身并不知道如何处理multipart的表单数据。我们需要一个multipart解析器把POST请求的multipart数据中抽取出来,这样DispatcherServlet就能将其传递给控制器。
为了在Spring中注册multipart解析器,需要声明一个实现了MultipartResolver接口的Bean。Spring只提供了一个multipart解析器
multipart解析器的Bean ID是有意义的。当DispatcherServlet查找multipart解析器的时候,它将会查找ID为multipart的解析器的Bean,如果multipart解析器是其他ID的话,DispatcherServlet将会忽略它
SpringMVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单。这些控制器一般不会直接处理请求,而是将其委托给Spring应用上下文中的其他Bean,通过Spring的依赖注入功能,这些Bean被注入到控制器中。
处理器映射会选择使用哪个控制器来处理请求,视图解析器会选择结果应该如何渲染。

我要回帖

更多关于 表单控制器 的文章

 

随机推荐