SpringMVC框架(一)

SpringMVC框架

第0章 SpringMVC框架的核心内容

SpringMVC框架(一)

第1章 SpringMVC 概述

1.1 SpringMVC 概述

1) Spring 为展现层提供的基于 MVC 设计理念的优秀的 Web 框架,是目前最主流的 MVC 框架之一
2)Spring3.0 后全面超越 Struts2,成为最优秀的 MVC 框架。
3)Spring MVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。
4)支持 REST 风格的 URL 请求。
5)采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。

1.2 SpringMVC是什么

1)一种轻量级的、基于MVC的Web层应用框架。偏前端而不是基于业务逻辑层。Spring框架的一个后续产品。
2)Spring框架结构图(新版本):
SpringMVC框架(一)

1.3 SpringMVC能干什么

1) 天生与Spring框架集成,如:(IOC,AOP)
2) 支持Restful风格
3) 进行更简洁的Web层开发
4) 支持灵活的URL到页面控制器的映射
5) 非常容易与其他视图技术集成,如:Velocity、FreeMarker等等
6) 因为模型数据不存放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用)
7) 非常灵活的数据验证、格式化和数据绑定机制、能使用任何对象进行数据绑定,不必实现特定框架的API
8) 更加简单、强大的异常处理
9) 对静态资源的支持
10) 支持灵活的本地化、主题等解析

1.4 SpringMVC怎么玩

1) 将Web层进行了职责解耦,基于请求-响应模型
2) 常用主要组件
① DispatcherServlet:前端控制器
② Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理
③ HandlerMapping :请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)
④ View Resolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,很容易更换其他视图技术;
 如InternalResourceViewResolver将逻辑视图名映射为JSP视图
⑤ LocalResolver:本地化、国际化
⑥ MultipartResolver:文件上传解析器
⑦ HandlerExceptionResolver:异常处理器

1.5 永远的HelloWorld

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
① 增加名称空间
SpringMVC框架(一)
② 增加配置

<!-- 设置扫描组件的包: -->
<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.6 HelloWorld深度解析

1) HelloWorld请求流程图解:
SpringMVC框架(一)
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)流程分析:
SpringMVC框架(一)
基本步骤:
① 客户端请求提交到DispatcherServlet
② 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
③ DispatcherServlet将请求提交到Controller(也称为Handler)
④ Controller调用业务逻辑处理后,返回ModelAndView
⑤ DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
⑥ 视图负责将结果显示到客户端

第2 章 @RequestMapping注解

2.1 @RequestMapping 映射请求注解

2.1.1 @RequestMapping 概念

1) SpringMVC使用@RequestMapping注解为控制器指定可以处理哪些 URL 请求
2) 在控制器的类定义及方法定义处都可标注 @RequestMapping
① 标记在类上:提供初步的请求映射信息。相对于 WEB 应用的根目录
② 标记在方法上:提供进一步的细分映射信息。相对于标记在类上的 URL(也就是@RequestMapping中的value)。
3) 若类上未标注 @RequestMapping,则方法处标记的 URL 相对于 WEB 应用的根目录
4) 作用:DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。

2.1.2 @ 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 {};
}

2.2 RequestMapping 可标注的位置

2.2.1 实验代码

定义页面链接、控制器方法

<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"; //结果如何跳转呢?需要配置视图解析器
}        
}

2.3 RequestMapping映射请求方式

2.3.1 标准的 HTTP 请求报头

SpringMVC框架(一)

2.3.2 映射请求参数、请求方法或请求头

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

2.3.3 实验代码

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>

2.4 RequestMapping映射请求参数&请求头

2.4.1 RequestMapping_请求参数&请求头【了解】

//了解: 可以使用 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";
}

2.4.2 实验代码

1) 请求URL

<!--设置请求参数和请求头信息 -->        
<a href="springmvc/testParamsAndHeaders">testParamsAndHeaders</a>

2) 测试:使用火狐或Chrom浏览器debug测试
① 测试有参数情况(不正确):
<a href="springmvc/testParamsAndHeaders">testParamsAndHeaders</a>
警告: No matching handler method found for servlet request: path ‘/springmvc/testParamsAndHeaders’, method ‘GET’, parameters map[[empty]]
<a href="springmvc/testParamsAndHeaders?username=atguigu&age=10">testParamsAndHeaders</a>
警告: No matching handler method found for servlet request: path ‘/springmvc/testParamsAndHeaders’, method ‘GET’, parameters map[‘username’ -> array[‘atguigu’], ‘age’ -> array[‘10’]]
<a href="springmvc/testParamsAndHeaders?age=11">testParamsAndHeaders</a>
警告: 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>

2.5 RequestMapping映射请求占位符PathVariable注解

2.5.1 @PathVariable

带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:
URL 中的 {xxx} 占位符可以通过 @PathVariable(“xxx”) 绑定到操作方法的入参中。

2.5.2 实验代码

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>

第3章 REST

3.1参考资料:

1)理解本真的REST架构风格: http://kb.cnblogs.com/page/186516/
2)REST: http://www.infoq.com/cn/articles/rest-introduction

3.2 REST是什么?

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 请求。

3.3 HiddenHttpMethodFilter过滤器源码分析

1) 为什么请求隐含参数名称必须叫做”_method”
SpringMVC框架(一)

2) hiddenHttpMethodFilter 的处理过程

SpringMVC框架(一)
SpringMVC框架(一)

3.4 实验代码

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>

第4章 处理请求数据

4.1请求处理方法签名

1) Spring MVC 通过分析处理方法的签名(方法的名字+参数列表),HTTP请求信息绑定到处理方法的相应形参中。
2) Spring MVC 对控制器处理方法签名的限制是很宽松的,几乎可以按喜欢的任何方式对方法进行签名。
3) 必要时可以对方法及方法入参标注相应的注解( @PathVariable 、@RequestParam、@RequestHeader 等)、
4) Spring MVC 框架会将 HTTP 请求的信息绑定到相应的方法入参中,并根据方法的返回值类型做出相应的后续处理。

4.2 @RequestParam注解

1)在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法
2)value:参数名
3)required:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常
4)defaultValue: 默认值,当没有传递参数时使用该值

4.2.1 实验代码

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>

4.3 @RequestHeader 注解

1) 使用 @RequestHeader 绑定请求报头的属性值
2) 请求头包含了若干个属性,服务器可据此获知客户端的信息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中

4.3.1 实验代码

//了解: 映射请求头信息 用法同 @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>

4.4 @CookieValue 注解

1) 使用 @CookieValue 绑定请求中的 Cookie 值
2) @CookieValue 可让处理方法入参绑定某个 Cookie 值

4.4.1实验代码

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>

4.5 使用POJO作为参数

1) 使用 POJO 对象绑定请求参数值
2) Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。支持级联属性。如:dept.deptId、dept.address.tel 等

4.5.1实验代码

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>

SpringMVC框架(一)
2) 增加实体类

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 
}

3) 执行结果:
SpringMVC框架(一)

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>

4.6 使用Servlet原生API作为参数

1) MVC 的 Handler 方法可以接受哪些 ServletAPI 类型的参数

  1. HttpServletRequest
  2. HttpServletResponse
  3. HttpSession
  4. java.security.Principal
  5. Locale
  6. InputStream
  7. OutputStream
  8. Reader
  9. Writer
    2) 源码参考:AnnotationMethodHandlerAdapter L866
    SpringMVC框架(一)
    3)
@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);
}

4.6.1 实验代码

/**
 * 可以使用 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>

第5章 处理响应数据

5.1 SpringMVC 输出模型数据概述

5.1.1提供了以下几种途径输出模型数据

1) ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据
2) Map 及 Model: 入参为 org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。
3) @SessionAttributes: 将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性
4) @ModelAttribute: 方法入参标注该注解后, 入参的对象就会放到数据模型中

5.2处理模型数据之 ModelAndView

5.2.1 ModelAndView介绍

1) 控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。
2)添加模型数据:
MoelAndView addObject(String attributeName, Object attributeValue)
ModelAndView addAllObject(Map<String, ?> modelMap)
3)设置视图:
void setView(View view)
void setViewName(String viewName)
4)获取model的三个方法
getModelInternal getModelMap getModel

5.2.2 实验代码

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 }

4) 断点调试
SpringMVC框架(一)

5.2.2 源码解析

SpringMVC框架(一)
SpringMVC框架(一)
SpringMVC框架(一)
SpringMVC框架(一)
SpringMVC框架(一)
SpringMVC框架(一)

5.3 处理模型数据之 Map

5.3.1 Map介绍

1)Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据
具体使用步骤
2)Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
3)如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。
4)在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据
SpringMVC框架(一)
SpringMVC框架(一)

5.3.2 实验代码

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) 显示结果截图
SpringMVC框架(一)
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) 类层次结构
SpringMVC框架(一)
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);
}
}
}
}
}
上一篇:linux常用命令


下一篇:《推荐系统:技术、评估及高效算法》一1.5 应用与评价