文章目录
Spring MVC起步
跟踪Spring MVC的请求
搭建Spring MVC
配置DispatcherServlet
DispatcherServlet是Spring MVC的核心。在这里请求会第一次 接触到框架,它要负责将请求路由到其他的组件之中。
在Servlet 3.0环境 中,容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer
接口的类,如果能发现的话,就会用它来配置Servlet容器。
Spring提供了这个接口的实现,名为SpringServletContainerInitializer
,这个类反过来又会 查找实现WebApplicationInitializer
的类并将配置的任务交给它们来完成。Spring 3.2引入了一个便利的WebApplicationInitializer
基础实现,也就是AbstractAnnotationConfigDispatcherServletInitializer
因为我们的Spittr-WebAppInitializer
扩展了 AbstractAnnotationConfig DispatcherServletInitializer
(同时也就实现了 WebApplicationInitializer
),因此当部署到Servlet 3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。
当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件或配置类中所声明的bean。
getServletConfigClasses()方法中,我们要 求DispatcherServlet加载应用上下文时,使用定义在WebConfig配置类(使用Java配置)中的bean。
但是在Spring Web应用中,通常还会有另外一个应用上下文。另外的 这个应用上下文是由ContextLoaderListener创建的。
我们希望DispatcherServlet加载包含Web组件的bean,如控制 器、视图解析器以及处理器映射,而ContextLoaderListener要 加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。
实际上,AbstractAnnotationConfigDispatcherServletInitializer
会同时创建DispatcherServlet
和 ContextLoaderListener
。GetServlet-ConfigClasses()
方法返回的带有@Configuration
注解的类将会用来定义DispatcherServlet
应用上下文中的 bean。getRootConfigClasses()
方法返回的带有@Configuration
注解的类将会用来配置ContextLoaderListener
创建的应用上下文中的bean。
启用Spring MVC
我们需要在WebConfig这个最小的Spring MVC配置上再加一 些内容,从而让它变得真正有用
编写基本的控制器
在Spring MVC中,控制器只是方法上添加了@RequestMapping注解 的类,这个注解声明了它们所要处理的请求。
假设控制器类要处理对“/”的请求, 并渲染应用的首页
你可以看到,home()方法其实并没有做太多的事情:它返回了一个String类型的“home”。这个String将会被Spring MVC解读为要 渲染的视图名称。DispatcherServlet会要求视图解析器将这个逻辑名称解析为实际的视图。
鉴于我们配置InternalResourceViewResolver的方式,视图 名“home”将会解析为“/WEB-INF/views/home.jsp”路径的JSP。
测试控制器
Spring现在包含了一种mock Spring MVC并针对控制器执行HTTP请求的机制。这样的话,在测试控制器的时候,就没有必要再启动Web服务器和Web浏览器了。
定义类级别的请求处理
在这个新版本的HomeController中,路径现在被转移到类级别的 @RequestMapping上,而HTTP方法依然映射在方法级别上。当控 制器在类级别上添加@RequestMapping注解时,这个注解会应用到 控制器的所有处理器方法上。处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping的声明进行补充
就HomeController而言,这里只有一个控制器方法。与类级别的 @Request-Mapping合并之后,这个方法的@RequestMapping表明home()将会处理对“/”路径的GET请求。
当我们在修改@RequestMapping时,还可以对HomeController 做另外一个变更。@RequestMapping的value属性能够接受一 个String类型的数组。到目前为止,我们给它设置的都是一个 String类型的“/”。但是,我们还可以将它映射到对“/homepage”的
请求,只需将类级别的@RequestMapping改为如下所示:
@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController{
//
}
传递模型数据到视图中
这个测试首先会创建SpittleRepository接口的mock实现,这个 实现会从它的findSpittles()方法中返回20个Spittle对象。然 后,它将这个Repository注入到一个新的SpittleController实例中,然后创建MockMvc并使用这个控制器。
Model实际上就是一个Map(也就是 key-value对的集合),它会传递给视图,这样数据就能渲染到客户端 了。当调用addAttribute()方法并且不指定key的时候,那么key会 根据值的对象类型推断确定。在本例中,因为它是一个List,因此,键将会推断为spittleList
如果你希望显式声明模型的key的话,那也尽可以进行指定
不管你选择哪种方式来编写spittles()方法,所达成的结果都是相 同的。模型中会存储一个Spittle列表,key为spittleList,然 后这个列表会发送到名为spittles的视图中。按照我们配 置InternalResourceViewResolver的方式,视图的JSP将会是“/WEB-INF/views/spittles.jsp”。
接受请求的输入
Spring MVC允许以多种方式将客户端中的数据传送到控制器的处理器 方法中,包括:
- 查询参数(Query Parameter)
- 表单参数(Form Parameter)
- 路径变量(Path Variable)
处理查询参数
用来测试分页Spittle列表的新方法:
用默认值的情况:@RequestParam注解的defaultValue属性
通过路径参数接受输入
到目前为止,在我们编写的控制器中,所有的方法都映射到了(通过 @RequestMapping)静态定义好的路径上。但是,如果想让这个测 试通过的话,我们编写的@RequestMapping要包含变量部分,这部
分代表了Spittle ID。
为了实现这种路径变量,Spring MVC允许我们在@RequestMapping 路径中添加占位符。占位符的名称要用大括号(“{”和“}”)括起来。 路径中的其他部分要与所处理的请求完全匹配,但是占位符部分可以是任意的值。
需要注意的是:在样例中spittleId这个词出现了好几次:先是 在@RequestMapping的路径中,然后作为@PathVariable属性的 值,最后又作为方法的参数名称。因为方法的参数名碰巧与占位符的名称相同,因此我们可以去掉@PathVariable中的value属性:
如果@PathVariable中没有value属性的话,它会假设占位符的名 称与方法的参数名相同。这能够让代码稍微简洁一些,因为不必重复写占位符的名称了。但需要注意的是,如果你想要重命名参数时,必须要同时修改占位符的名称,使其互相匹配。
处理表单
SpitterController:展现一个表单,允许用户注册该 应用
编写处理表单的控制器
测试:
processRegistration()方法做的最后一件事就是返回一 个String类型,用来指定视图。但是这个视图格式和以前我们所看 到的视图有所不同。这里不仅返回了视图的名称供视图解析器查找目标视图,而且返回的值还带有重定向的格式。
当InternalResourceViewResolver看到视图格式中 的“redirect:”前缀时,它就知道要将其解析为重定向的规则,而不是视图的名称
在本例中,它将会重定向到用户基本信息的页面。例 如,如果Spitter.username属性的值为“jbauer”,那么视图将会重定向到“/spitter/jbauer”
需要注意的是,除了“redirect:”,InternalResourceViewResolver还能识 别“forward:”前缀。当它发现视图格式中以“forward:”作为前缀
时,请求将会前往(forward)指定的URL路径,而不再是重定向。
校验表单
从Spring 3.0开 始,在Spring MVC中提供了对Java校验API的支持
与其让校验逻辑弄乱我们的处理器方法,还不如使用Spring对Java校验API的支持
Java校验API定义了多个注解,这些注解可以放到属性上,从而限制这些属性的值。所有的注解都位于javax.validation.constraints包中。
注解 | 描述 |
---|---|
@AssertFalse | 所注解的元素必须是Boolean类型,并且值为false |
@AssertTrue | 所注解的元素必须是Boolean类型,并且值为true |
@DecimalMax | 所注解的元素必须是数字,并且它的值要小于或等于给定的 BigDecimalString值 |
@DecimalMin | 所注解的元素必须是数字,并且它的值要大于或等于给定的 BigDecimalString值 |
@Digits | 所注解的元素必须是数字,并且它的值必须有指定的位数 |
@Future | 所注解的元素的值必须是一个将来的日期 |
@Max | 所注解的元素必须是数字,并且它的值要小于或等于给定的值 |
@Min | 所注解的元素必须是数字,并且它的值要大于或等于给定的值 |
@NotNull | 所注解元素的值必须不能为null |
@Null | 所注解元素的值必须为null |
@Past | 所注解的元素的值必须是一个已过去的日期 |
@Pattern | 所注解的元素的值必须匹配给定的正则表达式 |
@Size | 所注解的元素的值必须是String、集合或数组,并且它的长度要符合给定的范围 |
我们已经为Spitter添加了校验注解,接下来需要修改processRegistration()方法来应用校验功能。启用校验功能
的processRegistration()如下所示:
很重要一点需要注意,Errors参数要紧跟在带有@Valid注解的参数后面,@Valid注解所标注的就是要检验的参数。
processRegistration()方法所做的第一件事就是调 用Errors.hasErrors()来检查是否有错误。
如果有错误的话,Errors.hasErrors()将会返回到registerForm,也就是注册表单的视图