1) Spring 为展现层提供的基于 MVC 设计理念的优秀的 Web 框架,是目前最主流的 MVC 框架之一
2)Spring3.0 后全面超越 Struts2,成为最优秀的 MVC 框架。
3)Spring MVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。
4)支持 REST 风格的 URL 请求。
5)采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。
1)一种轻量级的、基于MVC的Web层应用框架。偏前端而不是基于业务逻辑层。Spring框架的一个后续产品。
2)Spring框架结构图(新版本):
1) 天生与Spring框架集成,如:(IOC,AOP)
2) 支持Restful风格
3) 进行更简洁的Web层开发
4) 支持灵活的URL到页面控制器的映射
5) 非常容易与其他视图技术集成,如:Velocity、FreeMarker等等
6) 因为模型数据不存放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用)
7) 非常灵活的数据验证、格式化和数据绑定机制、能使用任何对象进行数据绑定,不必实现特定框架的API
8) 更加简单、强大的异常处理
9) 对静态资源的支持
10) 支持灵活的本地化、主题等解析
1) 将Web层进行了职责解耦,基于请求-响应模型
2) 常用主要组件
① DispatcherServlet:前端控制器
② Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理
③ HandlerMapping :请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)
④ View Resolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,很容易更换其他视图技术;
如InternalResourceViewResolver将逻辑视图名映射为JSP视图
⑤ LocalResolver:本地化、国际化
⑥ MultipartResolver:文件上传解析器
⑦ HandlerExceptionResolver:异常处理器
1) 新建Web工程,加入 jar 包
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
2) 在 web.xml 中配置 DispatcherServlet
<!-- 配置SpringMVC核心控制器: -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置DispatcherServlet的初始化參數:设置文件的路径和文件名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
① 解释配置文件的名称定义规则:
实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.默认的配置文件为: /WEB-INF/-servlet.xml
3) 加入 Spring MVC 的配置文件:springmvc.xml
① 增加名称空间
② 增加配置
<!-- 设置扫描组件的包: -->
<context:component-scan base-package="com.atguigu.springmvc"/>
<!-- 配置映射解析器:如何将控制器返回的结果字符串,转换为一个物理的视图文件-->
<bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
4) 需要创建一个入口页面,index.jsp
<a href="${pageContext.request.contextPath }/helloworld">Hello World</a>
5) 编写处理请求的处理器,并标识为处理器
package com.atguigu.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller //声明Bean对象,为一个控制器组件
public class HelloWorldController {
/**
* 映射请求的名称:用于客户端请求;类似Struts2中action映射配置的action名称
* 1. 使用 @RequestMapping 注解来映射请求的 URL
* 2. 返回值会通过视图解析器解析为实际的物理视图, 对于 InternalResourceViewResolver 视图解析器,
* 会做如下的解析:
* 通过 prefix + returnVal + suffix 这样的方式得到实际的物理视图, 然后做转发操作.
* /WEB-INF/views/success.jsp
*/
@RequestMapping(value="/helloworld",method=RequestMethod.GET)
public String helloworld(){
System.out.println("hello,world");
return "success"; //结果如何跳转呢?需要配置映射解析器
}
}
6) 编写视图
/WEB-INF/views/success.jsp
<h4>Sucess Page</h4>
7) 部署测试:
http://localhost:8080/SpringMVC_01_HelloWorld/index.jsp
1) HelloWorld请求流程图解:
2) 一般请求的映射路径名称和处理请求的方法名称最好一致(实质上方法名称任意)
@RequestMapping(value="/helloworld",method=RequestMethod.GET)
public String helloworld(){
//public String abc123(){
System.out.println("hello,world");
return "success";
}
3) 演示一个错误
经常有同学会出现配置上错误,把“/WEB-INF/views/”配置成了 “/WEB-INF/views”
<bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
4) 处理请求方式有哪几种
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
5) @RequestMapping可以应用在什么地方
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {…}
6)流程分析:
基本步骤:
① 客户端请求提交到DispatcherServlet
② 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
③ DispatcherServlet将请求提交到Controller(也称为Handler)
④ Controller调用业务逻辑处理后,返回ModelAndView
⑤ DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
⑥ 视图负责将结果显示到客户端
1) SpringMVC使用@RequestMapping注解为控制器指定可以处理哪些 URL 请求
2) 在控制器的类定义及方法定义处都可标注 @RequestMapping
① 标记在类上:提供初步的请求映射信息。相对于 WEB 应用的根目录
② 标记在方法上:提供进一步的细分映射信息。相对于标记在类上的 URL(也就是@RequestMapping中的value)。
3) 若类上未标注 @RequestMapping,则方法处标记的 URL 相对于 WEB 应用的根目录
4) 作用:DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。
package org.springframework.web.bind.annotation;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
定义页面链接、控制器方法
<a href="springmvc/helloworld">test @RequestMapping</a>
@Controller //声明Bean对象,为一个控制器组件
@RequestMapping("/springmvc")
public class HelloWorldController {
/**
* 映射请求的名称:用于客户端请求;类似Struts2中action映射配置的,action名称
*1 使用@RequestMapping 注解来映射请求的 URL
*2 返回值会通过视图解析器解析为实际的物理视图,
* 对于 InternalResourceViewResolver 视图解析器,
* 会做如下的解析:
* 通过 prefix + returnVal + 后缀 这样的方式得到实际的物理视图, 然会做转发操作.
* /WEB-INF/views/success.jsp
*/
@RequestMapping(value="/helloworld")
public String helloworld(){
System.out.println("hello,world");
return "success"; //结果如何跳转呢?需要配置视图解析器
}
}
1)@RequestMapping 除了可以使用请求 URL 映射请求外,还可以使用请求方法、请求参数及请求头映射请求
2)@RequestMapping 的 value【重点】、method【重点】、params【了解】 及 heads【了解】 分别表示请求 URL、请求方法、请求参数及请求头的映射条件,他们之间是与的关系,联合使用多个条件可让请求映射更加精确化。
3)params 和 headers支持简单的表达式:
param1: 表示请求必须包含名为 param1 的请求参数
!param1: 表示请求不能包含名为 param1 的请求参数
param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
{"param1=value1", "param2"}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
1) 定义控制器方法
@Controller
@RequestMapping("/springmvc")
public class SpringMVCController {
@RequestMapping(value="/testMethord",method=RequestMethod.POST)
public String testMethord(){
System.out.println("testMethord...");
return "success";
}
}
2) 以get方式请求
<a href="springmvc/testMethord">testMethord</a>
发生请求错误
3) 以POST方式请求
<form action="springmvc/testMethord" method="post">
<input type="submit" value="submit">
</form>
//了解: 可以使用 params 和 headers 来更加精确的映射请求. params 和 headers 支持简单的表达式.
@RequestMapping(value="/testParamsAndHeaders",
params= {"username","age!=10"}, headers = { "Accept-Language=en-US,zh;q=0.8" })
public String testParamsAndHeaders(){
System.out.println("testParamsAndHeaders...");
return "success";
}
1) 请求URL
<!--设置请求参数和请求头信息 -->
<a href="springmvc/testParamsAndHeaders">testParamsAndHeaders</a>
2) 测试:使用火狐或Chrom浏览器debug测试
① 测试有参数情况(不正确):
testParamsAndHeaders
警告: No matching handler method found for servlet request: path ‘/springmvc/testParamsAndHeaders’, method ‘GET’, parameters map[[empty]]
testParamsAndHeaders
警告: No matching handler method found for servlet request: path ‘/springmvc/testParamsAndHeaders’, method ‘GET’, parameters map[‘username’ -> array[‘atguigu’], ‘age’ -> array[‘10’]]
testParamsAndHeaders
警告: No matching handler method found for servlet request: path ‘/springmvc/testParamsAndHeaders’, method ‘GET’, parameters map[‘age’ -> array[‘11’]]
② 测试有参数情况(正确):
<a href="springmvc/testParamsAndHeaders?username=atguigu&age=15">testParamsAndHeaders</a>
带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:
URL 中的 {xxx} 占位符可以通过 @PathVariable(“xxx”) 绑定到操作方法的入参中。
1) 定义控制器方法
//@PathVariable 注解可以将请求URL路径中的请求参数,传递到处理请求方法的入参中
@RequestMapping(value="/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") Integer id){
System.out.println("testPathVariable...id="+id);
return "success";
}
2) 请求链接
<!-- 测试 @PathVariable -->
<a href="springmvc/testPathVariable/1">testPathVariable</a>
1)理解本真的REST架构风格: http://kb.cnblogs.com/page/186516/
2)REST: http://www.infoq.com/cn/articles/rest-introduction
1) REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行
的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用
① 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。
它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。
可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。
获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
② 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
③ 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)
而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。
④ 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
2)URL风格
示例:
order/1 HTTP GET :得到 id = 1 的 order
order/1 HTTP DELETE:删除 id = 1的 order
order HTTP PUT:更新order
order HTTP POST:新增 order
3)HiddenHttpMethodFilter
浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不
支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使
得支持 GET、POST、PUT 与 DELETE 请求。
2) hiddenHttpMethodFilter 的处理过程
1) 配置HiddenHttpMethodFilter过滤器
<!-- 支持REST风格的过滤器:可以将POST请求转换为PUT或DELETE请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2) 代码
/**
* 1.测试REST风格的 GET,POST,PUT,DELETE 操作
* 以CRUD为例:
* 新增: /order POST
* 修改: /order/1 PUT update?id=1
* 获取: /order/1 GET get?id=1
* 删除: /order/1 DELETE delete?id=1
* 2.如何发送PUT请求或DELETE请求?
* ①.配置HiddenHttpMethodFilter
* ②.需要发送POST请求
* ③.需要在发送POST请求时携带一个 name="_method"的隐含域,值为PUT或DELETE
* 3.在SpringMVC的目标方法中如何得到id值呢?
* 使用@PathVariable注解
*/
@RequestMapping(value="/testRESTGet/{id}",method=RequestMethod.GET)
public String testRESTGet(@PathVariable(value="id") Integer id){
System.out.println("testRESTGet id="+id);
return "success";
}
@RequestMapping(value="/testRESTPost",method=RequestMethod.POST)
public String testRESTPost(){
System.out.println("testRESTPost");
return "success";
}
@RequestMapping(value="/testRESTPut/{id}",method=RequestMethod.PUT)
public String testRESTPut(@PathVariable("id") Integer id){
System.out.println("testRESTPut id="+id);
return "success";
}
@RequestMapping(value="/testRESTDelete/{id}",method=RequestMethod.DELETE)
public String testRESTDelete(@PathVariable("id") Integer id){
System.out.println("testRESTDelete id="+id);
return "success";
}
3) 请求链接
<!-- 实验1 测试 REST风格 GET 请求 -->
<a href="springmvc/testRESTGet/1">testREST GET</a><br/><br/>
<!-- 实验2 测试 REST风格 POST 请求 -->
<form action="springmvc/testRESTPost" method="POST">
<input type="submit" value="testRESTPost">
</form>
<!-- 实验3 测试 REST风格 PUT 请求 -->
<form action="springmvc/testRESTPut/1" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="testRESTPut">
</form>
<!-- 实验4 测试 REST风格 DELETE 请求 -->
<form action="springmvc/testRESTDelete/1" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="testRESTDelete">
</form>
1) Spring MVC 通过分析处理方法的签名(方法的名字+参数列表),HTTP请求信息绑定到处理方法的相应形参中。
2) Spring MVC 对控制器处理方法签名的限制是很宽松的,几乎可以按喜欢的任何方式对方法进行签名。
3) 必要时可以对方法及方法入参标注相应的注解( @PathVariable 、@RequestParam、@RequestHeader 等)、
4) Spring MVC 框架会将 HTTP 请求的信息绑定到相应的方法入参中,并根据方法的返回值类型做出相应的后续处理。
1)在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法
2)value:参数名
3)required:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常
4)defaultValue: 默认值,当没有传递参数时使用该值
1) 增加控制器方法
/**
* @RequestParam 注解用于映射请求参数
* value 用于映射请求参数名称
* required 用于设置请求参数是否必须的
* defaultValue 设置默认值,当没有传递参数时使用该值
*/
@RequestMapping(value="/testRequestParam")
public String testRequestParam(
@RequestParam(value="username") String username,
@RequestParam(value="age",required=false,defaultValue="0") int age){
System.out.println("testRequestParam - username="+username +",age="+age);
return "success";
}
2) 增加页面链接
<!--测试 请求参数 @RequestParam 注解使用 -->
<a href="springmvc/testRequestParam?username=atguigu&age=10">testRequestParam</a>
1) 使用 @RequestHeader 绑定请求报头的属性值
2) 请求头包含了若干个属性,服务器可据此获知客户端的信息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中
//了解: 映射请求头信息 用法同 @RequestParam
@RequestMapping(value="/testRequestHeader")
public String testRequestHeader(@RequestHeader(value="Accept-Language") String al){
System.out.println("testRequestHeader - Accept-Language:"+al);
return "success";
}
<!-- 测试 请求头@RequestHeader 注解使用 -->
<a href="springmvc/testRequestHeader">testRequestHeader</a>
1) 使用 @CookieValue 绑定请求中的 Cookie 值
2) @CookieValue 可让处理方法入参绑定某个 Cookie 值
1) 增加控制器方法
//了解:@CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
System.out.println("testCookieValue: sessionId: " + sessionId);
return "success";
}
2) 增加页面链接
<!--测试 请求Cookie @CookieValue 注解使用 -->
<a href="springmvc/testCookieValue">testCookieValue</a>
1) 使用 POJO 对象绑定请求参数值
2) Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。支持级联属性。如:dept.deptId、dept.address.tel 等
1) 增加控制器方法、表单页面
/**
* Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配, 自动为该对象填充属性值。
* 支持级联属性
* 如:dept.deptId、dept.address.tel 等
*/
@RequestMapping("/testPOJO")
public String testPojo(User user) {
System.out.println("testPojo: " + user);
return "success";
}
<!-- 测试 POJO 对象传参,支持级联属性 -->
<form action=" testPOJO" method="POST">
username: <input type="text" name="username"/><br>
password: <input type="password" name="password"/><br>
email: <input type="text" name="email"/><br>
age: <input type="text" name="age"/><br>
city: <input type="text" name="address.city"/><br>
province: <input type="text" name="address.province"/>
<input type="submit" value="Submit"/>
</form>
package com.atguigu.springmvc.entities;
public class Address {
private String province;
private String city;
//get/set
}
package com.atguigu.springmvc.entities;
public class User {
private Integer id ;
private String username;
private String password;
private String email;
private int age;
private Address address;
//get/set
}
4) 如果中文有乱码,需要配置字符编码过滤器,且配置其他过滤器之前,
如(HiddenHttpMethodFilter),否则不起作用。(思考method=”get”请求的乱码问题怎么解决的)
<!-- 配置字符集 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1) MVC 的 Handler 方法可以接受哪些 ServletAPI 类型的参数
@Override
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class.equals(parameterType)) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
eturn response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
/**
* 可以使用 Serlvet 原生的 API 作为目标方法的参数 具体支持以下类型
*
* HttpServletRequest
* HttpServletResponse
* HttpSession
* java.security.Principal
* Locale InputStream
* OutputStream
* Reader
* Writer
* @throws IOException
*/
@RequestMapping("/testServletAPI")
public void testServletAPI(HttpServletRequest request,HttpServletResponse response, Writer out) throws IOException {
System.out.println("testServletAPI, " + request + ", " + response);
out.write("hello springmvc");
//return "success";
}
<!-- 测试 Servlet API 作为处理请求参数 -->
<a href="springmvc/testServletAPI">testServletAPI</a>
1) ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据
2) Map 及 Model: 入参为 org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。
3) @SessionAttributes: 将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性
4) @ModelAttribute: 方法入参标注该注解后, 入参的对象就会放到数据模型中
1) 控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。
2)添加模型数据:
MoelAndView addObject(String attributeName, Object attributeValue)
ModelAndView addAllObject(Map
3)设置视图:
void setView(View view)
void setViewName(String viewName)
4)获取model的三个方法
getModelInternal getModelMap getModel
1) 增加控制器方法
/**
* 目标方法的返回类型可以是ModelAndView类型
* 其中包含视图信息和模型数据信息
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
System.out.println("testModelAndView");
String viewName = "success";
ModelAndView mv = new ModelAndView(viewName );
mv.addObject("time",new Date().toString()); //实质上存放到request域中
return mv;
}
2) 增加页面链接
<!--测试 ModelAndView 作为处理返回结果 -->
<a href="springmvc/testModelAndView">testModelAndView</a>
3) 增加成功页面,显示数据
time: ${requestScope.time }
1)Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据
具体使用步骤
2)Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
3)如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。
4)在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据
1) 增加控制器方法
//目标方法的返回类型也可以是一个Map类型参数(也可以是Model,或ModelMap类型)
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){ //【重点】
System.out.println(map.getClass().getName());
//org.springframework.validation.support.BindingAwareModelMap
map.put("names", Arrays.asList("Tom","Jerry","Kite"));
return "success";
}
2) 增加页面链接
<!-- 测试 Map 作为处理返回结果 -->
<a href="springmvc/testMap">testMap</a>
3) 增加成功页面,显示结果
names: ${requestScope.names }
4) 显示结果截图
5) 注意问题:Map集合的泛型,key为String,Value为Object,而不是String
6) 测试参数类型
//目标方法的返回类型也可以是一个Map类型参数(也可以是Model,或ModelMap类型)
@RequestMapping("/testMap2")
public String testMap2(Map<String,Object> map,Model model,ModelMap modelMap){
System.out.println(map.getClass().getName());
map.put("names", Arrays.asList("Tom","Jerry","Kite"));
model.addAttribute("model", "org.springframework.ui.Model");
modelMap.put("modelMap", "org.springframework.ui.ModelMap");
System.out.println(map == model);
System.out.println(map == modelMap);
System.out.println(model == modelMap);
System.out.println(map.getClass().getName());
System.out.println(model.getClass().getName());
System.out.println(modelMap.getClass().getName());
/*
true
true
true
org.springframework.validation.support.BindingAwareModelMap
org.springframework.validation.support.BindingAwareModelMap
org.springframework.validation.support.BindingAwareModelMap
*/
return "success";
}
7) 类层次结构
8) 推荐:Map, 便于框架移植。
9) 源码参考
public class BindingAwareModelMap extends ExtendedModelMap {
@Override
public Object put(String key, Object value) {
removeBindingResultIfNecessary(key, value);
return super.put(key, value);
}
@Override
public void putAll(Map<? extends String, ?> map) {
for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
removeBindingResultIfNecessary(entry.getKey(), entry.getValue());
}
super.putAll(map);
}
private void removeBindingResultIfNecessary(Object key, Object value) {
if (key instanceof String) {
String attributeName = (String) key;
if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attributeName;
BindingResult bindingResult = (BindingResult) get(bindingResultKey);
if (bindingResult != null && bindingResult.getTarget() != value) {
remove(bindingResultKey);
}
}
}
}
}