构建Spring Web应用程序
- 映射请求到Spring控制器
- 透明地绑定表单参数
- 校验表单提交
Spring MVC 基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,帮助构建灵活和松耦合的Web应用程序
Spring MVC起步
Spring MVC 请求流程
Spring MVC框架将请求在调度Servler、处理器映射(handler mapping)、控制器以及视图解析器(view resolver)之间移动
1:请求访问前端控制器(DispatcherServlet);
2:DispatcherServlet查询一个或多个处理器映射(handler mapping),通过请求路径确定控制器(Controller);
3:DispatcherServlet将请求发送给控制器(Controller);
4:控制器对请求进行处理返回模型和视图名(ModelAndView);
5:DispatcherServlet查询视图解析器(view resolver),通过视图名匹配一个特定的视图实现;
6:DispatcherServlet将模型发送给视图;
7:视图通过模型数据渲染输出,通过响应对象传递给客户端;
搭建Spring MVC
配置DisPatcherServlet
DisPatcherServlet是Spring MVC的核心。负责将请求路由到指定控制器处理。
传统方式,DispathcherServlet会配置在web.xml文件中;
借助于Servlet 3规范和Spring3.1功能增强,可实现Java配置DispatcherServlet
1 | package demo; |
原理:
1:在Serlvet 3环境中,Servlet容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,用来配置Servlet容器;
2:Spring通过SpringServletContainerInitializer实现了该接口,SpringServletContainerInitializer又会在容器中查找实现WebApplicationInitializer接口的类并将配置任务交给它们来完成;
3:Spring3.2引入一个便利的WebApplicationInitializer基础实现,即AbstractAnnotationConfigDispatcherServletIni-tializer,通过继承该类,当部署到Servler3.0容器中时,容器就会自动发现它,并用它来配置Servlet上下文。
继承AbstractAnnotationConfigDispatcherServletInitializer需重写三个方法:
1:getServletMappings() 设置DispatcherServlet映射、
在讲另外两个方法之前,先了解一下Dispatcher和一个Servler监听器(ContextLoaderListener)的关系
关于两个应用上下文的关系
DispatcherServlet启动的时候,会创建Spring应用上下文容器,并加载配置文件或配置类中所声明的bean。
Spring Web应用中,通常还有会另外一个应用上下文,由ContextLoaderListener创建。
我们通常希望DispathcerServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射。
而ContextLoaderListener加载应用中其他bean,这些bean通常是加载驱动应用后端的中间层和数据层组件。
2:getServletConfigClasses() 返回@Configuration注解类并定义DispatcherServlet应用上下文中的bean。
3:getRootConfigClasses() 返回@Configuration注解类并定义ContextLoaderListener应用上下文中的bean。
启用Spring MVC
完成配置DispatcherServlet后,需启用Spring MVC。
传统方式:使用XML配置,使用mvn:annotation-driven启用注解驱动的Spring MVC
使用Java配置:@EnableWebMvc注解 启用Spring MVC
1 | package demo; |
编写基本的控制器(Controller)
1 | package demo.web; |
基于以上配置,我们可以使用一个简单的Spring MVC访问页面,
为助于读者理解,提供一份开发部署流程,仅供参考:
1 配置DispatcherServlet,编写DemoWebAppInitializer(src.main.java.demo包)
2 开启Spring MVC,编写WebConfig 和 RootConfig (src.main.java.demo包)
3 编写控制器,HomeController(src.main.java.demo.web包)
4 编写页面,home.jsp(src.main.webapp.WEB-INF.views)
4 打war包(此过程应该会提示缺少web.xml,可在WEB-INF下添加web.xml 或 配置maven-war-plugin插件忽略web.xml)
5 部署war包至tomcat即可(注意Tomcat版本。理论上至少需要支持Servlet 3的Tomcat容器)
测试控制器
1 | //关于断言 |
上述测试仅调用home(),并断言返回包含”home”值的String,而不是站在Spring MVC控制器的视角进行测试。
正确的测试应该是发送”/“的GET请求会调用home()方法,并真正判断”home”是视图的名称。
Spring3.2开始支持第控制器视角的测试。通过mock Spring MVC并针对控制器执行HTTP请求的测试机制。
1 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; |
请求映射处理
1 | @Controller |
@RequestMapping({“/“,”/demo”}) 支持多个路径映射
传递模型数据到视图
有三种方式
1 | //方式1 Model(本质是一个Map,即 key-value 对的集合) |
接受请求的输入
Spring MVC允许以多种方式传递参数
1:查询参数(Query Parameter)
2:表单参数(Form Parameter)
3:路径变量(Path Variable)
处理查询参数
1基本类型 2 包装类型 3 java bean
默认以参数名接参
@RequestParam
value、name 设置参数名(与请求参数名匹配)
required 设置参数是否必传
defaultValue 设置默认值
通过路径参数接受输入
1 设置路径参数占位符
@RequestMapping(“/book/{id}”)
2 接受路径参数
public String getBook(@PathVariable Long id) {…}
@PathVariable
value、name 设置参数名(与路径参数名匹配)
required 设置参数是否必传
传输少量数据时,查询参数 和 路径参数都很适合
传递很多数据时,通常是表单提交的数据
处理表单
接受表单数据
表单的数据往往较多,可使用java Bean接受参数,Spring MVC会将请求参数中与bean属性同名的进行赋值,并返回bean实例。
检验表单数据
避免校验逻辑弄乱处理器代码,可使用Spring对Java校验API(Java Validation API,又称JSR-303)的支持。
Spring3.0开始,Spring MVC中提供了对Java校验API的支持。只需导入该Java API的实现即可,如Hibernate Validator
将注解定义到Java Bean字段属性上,在接受参数处对待检验的参数添加 @Valid 即可
@AssertFalse | 所注解的元素必须为Boolean类型,且值为false |
---|---|
@AssertTrue | 所注解的元素必须为Boolean类型,且值为true |
@DecimalMax | 所注解的元素必须为数字,且值要小于或等于给定的BigDecimalString的值 |
@DecimalMin | 所注解的元素必须为数字,且值要大于或等于给定的BigDecimalString的值 |
@Max | 所注解的元素必须为数字,并且它的值要小于或等于给定的值 |
@Min | 所注解的元素必须为数字,并且它的值要大于或等于给定的值 |
@Digits | 所注解的元素必须为数字,且值必须有指定的位数 |
@Future | 所注解的元素必须为一个将来的日期 |
@Past | 所注解元素必须为一个已过去的日期 |
@NotNull | 所注解元素的值必须不能为null |
@Null | 所注解元素的值必须为null |
@Pattern | 所注解元素必须匹配给定的正则表达式 |
@Size | 所注解的元素的值必须是String、集合或数组,并且它的长度要符合给定的范围 |
校验出错时,可以通过Errors对象进行访问,此对象已作为processRegistration()方法参数。(需注意,Errors参数要紧跟在带有@Valid注解的参数后面)
processRegistration()方法所做的第一件事就是调用Errors.hasErrors()来检查是否有错误。例如:
1 | //...表示省略 |
forward 和 redirect
总结一句话:转发是服务器行为,重定向是客户端行为。
转发过程(forward):客户浏览器发送http请求–》web服务器接受此请求–》调用内部的一个方法在容器内部完成请求处理和转发动作—-》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向过程(redirect):客户浏览器发送http请求–àweb服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址—-》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。