SpringMVC笔记

记录一下SpringMVC的学习

第0章 SpringMVC框架的核心内容

1.SpringMVC 概述
2.SpringMVC 的 HelloWorld
3.使用 @RequestMapping 映射请求
4.映射请求参数 & 请求头
5.处理模型数据
6.视图和视图解析器
7.RESTful CRUD
8.SpringMVC 表单标签 & 处理静态资源
9.处理 JSON:使用 HttpMessageConverter
10.文件的上传
11.使用拦截器
12.SpringMVC 运行流程
13.在 Spring 的环境下使用 SpringMVC

第1章 SpringMVC 概述

1.1SpringMVC 概述

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框架结构图(新版本):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K09CqbUl-1594558772635)(/Users/mac/Documents/寒假学习/大数据/SpringMVC/SpringMVC笔记/wpsbjeKdo.jpg)]

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

Springmvc的HelloWorld:

  1. 创建web工程, 导入核心的jar包.
  2. 在web.xml中配置SpringMVC的前端控制器: DispatcherServlet
  3. 配置SpringMVC的核心配置文件: springmvc.xml
  4. 编写请求处理器
  5. 浏览器端发送请求, 请求处理器中处理请求
  6. 测试

Springmvc HelloWorld 执行流程:

  1. 启动Tomcat服务器, 会加载DispatcherServlet, 然后就会读取springmvc.xml,进而创建好的Springmvc容器对象.
    创建Springmvc容器对象: 组件扫描会 扫描到请求处理器, 以及请求处理中@RequestMapping注解,
    能得到具体的请求与请求处理器 中方法的映射。

  2. 客户端发送请求: http://localhost:8888/Springmvc01/hello

  3. 请求到达web.xml中与进行匹配, 匹配成功,就将请求交给DispatcherServlet

  4. DispatcherServlet根据请求 与 请求处理方法的映射, 将请求交给具体的请求处理器中的请求处理方法来进行处理

  5. 请求处理方法处理完请求, 最终方法会返回一个字符串

  6. 视图解析器根据请求处理方法返回的结果, prefix + returnValue + suffix, 解析生成具体的物理视图路径,
    再通过转发的方式去往视图。

·

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-name>-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>

可以是:

<a href="hello">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/index.jsp

1.6 HelloWorld深度解析

1.6.1 HelloWorld请求流程图解:

在这里插入图片描述

1.6.2 一般请求的映射路径名称和处理请求的方法名称最好一致(实质上方法名称任意)
@RequestMapping(value="/helloworld",method=RequestMethod.GET)
public String helloworld(){
//public String abc123(){
System.out.println("hello,world");
return "success";
}
<bean id="internalResourceViewResolver" 
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
1.6.3 处理请求方式有哪几种
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
1.6.4 @RequestMapping可以应用在什么地方
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {}
1.6.5 流程分析:

在这里插入图片描述

基本步骤:
①客户端请求提交到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
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 请求报头

在这里插入图片描述

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,param2参数的值无限制
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.5 RequestMapping映射请求占位符PathVariable注解

2.5.1 @PathVariable
带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:
URL 中的 {xxx} 占位符可以通过 @PathVariable("xxx") 绑定到操作方法的入参中。
2.5.2 实验代码
  • 定义控制器方法
//@PathVariable 注解可以将请求URL路径中的请求参数,传递到处理请求方法的入参中
@RequestMapping(value="/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") Integer id){
System.out.println("testPathVariable...id="+id);
return "success";
}
  • 请求链接
<!-- 测试 @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.2REST是什么?

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”

在这里插入图片描述

2) hiddenHttpMethodFilter 的处理过程

在这里插入图片描述

在这里插入图片描述

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=learn&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 等
  • 3)请求参数必须和必须和实体类中的成员变量名相同,且必须提供get set方法
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";
}
<form action="testPOJO" method="post">
		用户名称: <input type="text" name="username"/>
		<br/>
		用户密码: <input type="password" name="password"/>
		<br/>
		用户邮箱: <input type="text" name="email"/>
		<br/>
		用户性别: 男 <input type="radio" name="gender" value="1"/><input type="radio" name="gender" value="0"/>
		<br/>
		<!-- 支持级联的方式 -->
		用户省份: <input type="text" name="address.province" />
		<br/>
		用户城市: <input type="text" name="address.city"/>
		<br/>
		<input type="submit" value="注册"/>
	</form>

在这里插入图片描述

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)如果中文有乱码,需要配置字符编码过滤器,且配置其他过滤器之前,
如(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
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 中的数据会自动添加到模型中。

5.2处理模型数据之 ModelAndView

5.2.1ModelAndView介绍
  • 1)控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。
  • 2)添加模型数据:
    MoelAndView addObject(String attributeName, Object attributeValue)
    ModelAndView addAllObject(Map<String, ?> modelMap)
  • 3)设置视图:
    void setView(View view)
    void setViewName(String viewName)
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="testModelAndView">testModelAndView</a>

3)增加成功页面,显示数据

time: ${requestScope.time }

4) 断点调试

断点调试

5.2.2 源码解析

1
2
3
4
5
6

5.3 处理模型数据之 Map

5.3.1Map介绍
  • 1)Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据
    具体使用步骤
  • 2)Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
  • 3)如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。
  • 4)在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据
    在这里插入图片描述
    在这里插入图片描述
    ExtendedModelMap和BindingAwareModelMap都实现了接口ModelMap
5.3.2 实验代码

1)增加控制器方法

结论:
SpringMVC会把Map中的模型数据存放到request域对象中.
SpringMVC再调用完请求处理方法后,不管方法的返回值是什么类型,都会处理成一个ModelAndView对象(参考DispatcherServlet的945行)

/**
	 * Map
	 * 结论: SpringMVC会把Map中的模型数据存放到request域对象中.
	 *      SpringMVC再调用完请求处理方法后,不管方法的返回值是什么类型,都会处理成一个ModelAndView对象(参考DispatcherServlet的945行)
	 * 
	 */		
	@RequestMapping("/testMap")
	public String  testMap(Map<String,Object> map ) {
		//模型数据: password=123456
		System.out.println(map.getClass().getName()); //BindingAwareModelMap
		map.put("password", "123456");
		
		return "success";
	}
	

2)增加页面链接

<!-- 测试 Map 作为处理返回结果 -->
<a href="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";
}

在这里插入图片描述

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);
}
}
}
}
}

第6章 视图解析

6.1 SpringMVC如何解析视图概述

1)不论控制器返回一个String,ModelAndView,View都会转换为ModelAndView对象,由视图解析器解析视图,然后,进行页面的跳转。
视图对象进行重定向等后续处理

数据流转

2)视图解析源码分析:重要的两个接口

接口1View

接口2ViewResolver

3)断点调试源码

在这里插入图片描述

4) 流程图

在这里插入图片描述

6.2 视图和视图解析器

  • 1)请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图
  • 2)Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart等各种表现形式的视图
  • 3)对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦

6.3 视图

1)视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户,主要就是完成转发或者是重定向的操作.
2)为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口:

View

3)视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题

6.4 常用的视图实现类

视图实现类

6.5 JstlView

  • 1)若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView (断点调试,将JSTL的jar包增加到项目中,视图解析器会自动修改为JstlView)
  • 2)若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现

在这里插入图片描述

6.5.1 实验代码

1)增加jstl标签 jar包(断点调试,这时的View对象就是JstlView)

Jstl

6.6 视图解析器

  • 1)SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。
  • 2)视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象
  • 3)所有的视图解析器都必须实现 ViewResolver 接口:

ViewResolver

6.7 常用的视图解析器实现类

在这里插入图片描述

1)程序员可以选择一种视图解析器或混用多种视图解析器
2)每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高。
3)SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常
4)InternalResourceViewResolver
①JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器:

在这里插入图片描述

6.8mvc:view-controller标签

1)若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现

<!-- 直接配置响应的页面:无需经过控制器来执行结果 -->
<!-- 不经过Handler直接跳转页面 -->
	<mvc:view-controller path="testViewContorller" view-name="success"/> 
	<!-- view-name是对应方法中,返回的那一个字符串-->
	<!-- 使用了view-controlelr以后,会导致RequestMapping的映射失效,因此需要加上 annotation-driven的配置 -->
	<!-- 后面讲 -->
	<mvc:annotation-driven/>

​ 2)请求的路径:

http://localhost:8080/Springmvc02/testViewContorller

3)配置

<mvc:view-controller>

会导致其他请求路径失效
解决办法:

<!-- 在实际开发过程中都需要配置mvc:annotation-driven标签,后面讲,这里先配置上 -->
<mvc:annotation-driven/>

6.9 重定向

  • 1)关于重定向
    ①一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理
    ②如果返回的字符串中带 forward: 或 redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和 redirect: 当成指示符,其后的字符串作为 URL 来处理
    ③redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作
    ④forward:success.jsp:会完成一个到 success.jsp 的转发操作
  • 2)定义页面链接
<a href="springmvc/testRedirect">testRedirect</a>
  • 3)定义控制器方法
	/**
	 * 重定向
	 */
	@RequestMapping("/testRedirectView")
	public String testRedirectView() {
		
		return "redirect:/ok.jsp";
	}
  //return "forward:/index.jsp";
  }

4)源码分析:重定向原理

1.源码分析:重定向原理

重定向原理

2

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

return "forward:/index.jsp"

在这里插入图片描述

第7章 综合案例RESTRUL_CRUD

7.1 RESTRUL_CRUD_需求

7.1.1 显示所有员工信息

1)URI:emps
2)请求方式:GET
3)显示效果
在这里插入图片描述

7.1.2 添加操作-去往添加页面

1)显示添加页面:
2)URI:emp
3)请求方式:GET
4)显示效果

在这里插入图片描述

7.1.3 添加操作-添加员工

1)添加员工信息:
2)URI:emp
3)请求方式:POST
4)显示效果:完成添加,重定向到 list 页面。

在这里插入图片描述

7.1.4 删除操作

1)URL:emp/{id}
2)请求方式:DELETE
3)删除后效果:对应记录从数据表中删除

7.1.5 修改操作-去往修改页面

1)URI:emp/{id}
2)请求方式:GET
3)显示效果:回显表单。

7.1.6 修改操作-修改员工

1)URI:emp
2)请求方式:PUT
3)显示效果:完成修改,重定向到 list 页面。

7.1.7 相关的类

省略了Service层,为了教学方便
1)实体类:Employee、Department
2)Handler:EmployeeHandler

3)Dao:EmployeeDao、DepartmentDao

在这里插入图片描述

7.1.8 相关的页面

1)list.jsp
2)input.jsp
3)edit.jsp

7.2 搭建开发环境

1) 拷贝jar包

com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
commons-logging-1.1.3.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
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar

2) 创建配置文件:springmvc.xml 增加context,mvc,beans名称空间。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 配置扫描的包:com.atguigu.springmvc.crud -->
<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"></property>
</bean> 
</beans>

3)配置核心控制器:web.xml

<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>

4)将 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>

5)创建相关页面

/WEB-INF/views/list.jsp
index.jsp

6)增加实体类

在这里插入图片描述
7)增加DAO类
EmployeeDao

package com.atguigu.springmvc.crud.dao;
 
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
 
import com.atguigu.springmvc.crud.entities.Department;
import com.atguigu.springmvc.crud.entities.Employee;
 
@Repository
public class EmployeeDao {
 
private static Map<Integer, Employee> employees = null;
 
@Autowired
private DepartmentDao departmentDao;
 
static{
employees = new HashMap<Integer, Employee>();
 
employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
}
 
private static Integer initId = 1006;
 
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
} 
employee.setDepartment(departmentDao.getDepartment(
employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
}
 
public Collection<Employee> getAll(){
return employees.values();
}
 
public Employee get(Integer id){
return employees.get(id);
}
 
public void delete(Integer id){
employees.remove(id);
}
}	

DepartmentDao

package com.atguigu.springmvc.crud.dao;
 
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
 
import org.springframework.stereotype.Repository;
 
import com.atguigu.springmvc.crud.entities.Department;
 
@Repository
public class DepartmentDao {
 
private static Map<Integer, Department> departments = null;
 
static{
departments = new LinkedHashMap<Integer, Department>();
 
departments.put(101, new Department(101, "D-AA"));
departments.put(102, new Department(102, "D-BB"));
departments.put(103, new Department(103, "D-CC"));
departments.put(104, new Department(104, "D-DD"));
departments.put(105, new Department(105, "D-EE"));
}
 
public Collection<Department> getDepartments(){
return departments.values();
}
 
public Department getDepartment(Integer id){
return departments.get(id);
}
 
}

7.3 RESTRUL_CRUD_显示所有员工信息

1) 增加页面链接

<a href="empList">To Employee List</a>

2)增加处理器

package com.atguigu.springmvc.crud.handlers;
 
import java.util.Map;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.atguigu.springmvc.crud.dao.EmployeeDao;
 
@Controller
public class EmployeeHandler {
 
@Autowired
private EmployeeDao employeeDao ;
 
@RequestMapping("/empList")
public String empList(Map<String,Object> map){
map.put("empList", employeeDao.getAll());        //默认存放到request域中        
return "list";
} 
}

3) SpringMVC中没遍历的标签,需要使用jstl标签进行集合遍历增加jstl标签库jar包

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<c:if test="${empty requestScope.empList }">
对不起,没有找到任何员工!
</c:if>
<c:if test="${!empty requestScope.empList }">
<table border="1" cellpadding="10" cellspacing="0">                
<tr>
<td>EmpId</td>
<td>LastName</td>
<td>Gender</td>
<td>Email</td>
<td>DepartmentName</td>
<td>Edit</td>
<td>Delete</td>
</tr>
<c:forEach items="${requestScope.empList }" var="emp">
<tr>
<td>${emp.id }</td>
<td>${emp.lastName }</td>
<td>${emp.gender==0?"Female":"Male" }</td>
<td>${emp.email }</td>
<td>${emp.department.departmentName }</td>
<td><a href="">Edit</a></td>
<td><a href="">Delete</a></td>
</tr>                
</c:forEach>        
</table>
</c:if>
 
</body>
</html>

7.4 RESTRUL_CRUD_添加操作

1) 在list.jsp上增加连接

<a href="empInput">Add Employee</a>

2) 增加处理器方法

@RequestMapping(value="/empInput",method=RequestMethod.GET)
public String empInput(Map<String,Object> map){
map.put("deptList", departmentDao.getDepartments());
//解决错误:java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
Employee employee = new Employee();
//map.put("command", employee);
map.put("employee", employee);
return "add";
}

3)显示添加页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!-- 
1.为什么使用SpringMVC的form标签
① 快速开发
② 表单回显
2.可以通过modelAttribute指定绑定的模型属性,
若没有指定该属性,则默认从request域中查找command的表单的bean
如果该属性也不存在,那么,则会发生错误。
 -->
 <form:form action="empAdd" method="POST" modelAttribute="employee">
         LastName : <form:input path="lastName"/><br><br>
         Email : <form:input path="email"/><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
        Gender : <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br><br>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
                 <input type="submit" value="Submit"><br><br>
 </form:form> 
</body>
</html>

4)显示表单信息时,会报错:

HTTP Status 500 - 
 
type Exception report
message 
description The server encountered an internal error () that prevented it from fulfilling this request.
exception 
org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/views/add.jsp at line 18
15:                         ② 表单回显
16:          -->
17:          <form:form action="empAdd" method="POST">
18:                  LastName : <form:input path="lastName"/>
19:                  Email : <form:input path="email"/>
20:                  <%
21:                          Map<String,String> map = new HashMap<String,String>();
Stacktrace:
        org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:505)
        org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:410)
        org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:337)
        org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
        org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:209)
        org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:266)
        org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1225)
        org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1012)
        org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
        org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
        org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
        org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:822)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
        org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
        org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
root cause 
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
        org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)

7.5 使用Spring的表单标签

  • 1)通过 SpringMVC 的表单标签可以实现将模型数据中的属性和 HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显

  • 2)form 标签
    一般情况下,通过 GET 请求获取表单页面,而通过 POST 请求提交表单页面,因此获取表单页面和提交表单页面的 URL 是相同的。
    只要满足该最佳条件的契约,form:form 标签就无需通过 action 属性指定表单提交的 URL
    可以通过 modelAttribute 属性指定绑定的模型属性,若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean,如果该属性值也不存在,则会发生错误。

  • 3)SpringMVC 提供了多个表单组件标签,如 form:input/、form:select/ 等,用以绑定表单字段的属性值,它们的共有属性如下:
    path:表单字段,对应 html 元素的 name 属性,支持级联属性
    htmlEscape:是否对表单值的 HTML 特殊字符进行转换,默认值为 true
    cssClass:表单组件对应的 CSS 样式类名
    cssErrorClass:表单组件的数据存在错误时,采取的 CSS 样式

  • 4)form:input、form:password、form:hidden、form:textarea:对应 HTML 表单的 text、password、hidden、

    textarea 标签

  • 5)form:radiobutton:单选框组件标签,当表单 bean 对应的属性值和 value 值相等时,单选框被选中

  • 6)form:radiobuttons:单选框组标签,用于构造多个单选框
    items:可以是一个 List、String[] 或 Map
    itemValue:指定 radio 的 value 值。可以是集合中 bean 的一个属性值
    itemLabel:指定 radio 的 label 值
    delimiter:多个单选框可以通过 delimiter 指定分隔符

  • 7)form:checkbox:复选框组件。用于构造单个复选框

  • 8)form:checkboxs:用于构造多个复选框。使用方式同 form:radiobuttons 标签

  • 9)form:select:用于构造下拉框组件。使用方式同 form:radiobuttons 标签

  • 10)form:option:下拉框选项组件标签。使用方式同 form:radiobuttons 标签

  • 11)form:errors:显示表单组件或数据校验所对应的错误

    <form:errors path= “*” /> :显示表单所有的错误
    <form:errors path= “user*” /> :显示所有以 user 为前缀的属性对应的错误
    <form:errors path= “username” /> :显示特定表单对象属性的错误
    

7.6 增加员工实验代码

1) 表单

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!-- 
1.为什么使用SpringMVC的form标签
① 快速开发
② 表单回显
2.可以通过modelAttribute指定绑定的模型属性,
若没有指定该属性,则默认从request域中查找command的表单的bean
如果该属性也不存在,那么,则会发生错误。
 -->
 <form:form action="empAdd" method="POST" modelAttribute="employee">
         LastName : <form:input path="lastName" /><br><br>
         Email : <form:input path="email" /><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
         Gender : <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br><br>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
                 <input type="submit" value="Submit"><br><br>
 </form:form> 
</body>
</html>

2) 控制器方法

@Controller
public class EmployeeHandler {
@RequestMapping(value="/empAdd",method=RequestMethod.POST)
public String empAdd(Employee employee){
employeeDao.save(employee);
return "redirect:/empList";
}
}

7.7 RESTRUL_CRUD_删除操作&处理静态资源

7.7.1 删除实验代码

1)页面链接

<td><a href="/empDelete/${emp.id }">Delete</a></td>

2)控制器方法

@RequestMapping(value="/empDelete/{id}" ,method=RequestMethod.DELETE)
public String empDelete(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/empList";
}
7.7.2 HiddenHttpMethodFilter过滤器

发起请求,无法执行,因为delete请求必须通过post请求转换为delete请求,借助:HiddenHttpMethodFilter过滤器

7.7.3 需要使用jQuery来转换请求方式

1)加入jQuery库文件

/scripts/jquery-1.9.1.min.js

2)jQuery库文件不起作用

警告: No mapping found for HTTP request with URI [/SpringMVC_03_RESTFul_CRUD/scripts/jquery-1.9.1.min.js] in DispatcherServlet with name ‘springDispatcherServlet’

在这里插入图片描述

3)解决办法,SpringMVC 处理静态资源
①为什么会有这样的问题:
优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀,若将 DispatcherServlet 请求映射配置为 /, 则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理, 因找不到对应处理器将导致错误。
②解决: 在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>
4)配置后,原来的请求又不好使了
需要配置<mvc:annotation-driven />

7.7.4 关于mvc:default-servlet-handler/作用
<!-- 
<mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,
就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理
一般 WEB 应用服务器默认的 Servlet 的名称都是 default。
若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定        
参考:CATALINA_HOME/config/web.xml
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
该标签属性default-servlet-name默认值是"default",可以省略。        
<mvc:default-servlet-handler/>        
 -->
<mvc:default-servlet-handler default-servlet-name="default"/>
7.7.5 通过jQuery转换为DELETE请求
<td><a class="delete" href="empDelete/${emp.id }">Delete</a></td>
<form action="" method="post">
<input type="hidden" name="_method" value="DELETE"/>
</form>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
$(".delete").click(function(){
var href = $(this).attr("href");
$("form").attr("action",href).submit();
return false ;
});
});
</script>
7.7.6 删除操作流程图解

在这里插入图片描述

7.8 RESTRUL_CRUD_修改操作

7.8.1 根据id查询员工对象,表单回显

1) 页面链接

<td><a href="empEdit/${emp.id }">Edit</a></td>

2)控制器方法

//修改员工 - 表单回显
@RequestMapping(value="/empEdit/{id}",method=RequestMethod.GET)
public String empEdit(@PathVariable("id") Integer id,Map<String,Object> map){
map.put("employee", employeeDao.get(id));
map.put("deptList",departmentDao.getDepartments());
return "edit";
}

3)修改页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!-- 
1.为什么使用SpringMVC的form标签
① 快速开发
② 表单回显
2.可以通过modelAttribute指定绑定的模型属性,
若没有指定该属性,则默认从request域中查找command的表单的bean
如果该属性也不存在,那么,则会发生错误。
修改功能需要增加绝对路径,相对路径会报错,路径不对
 -->
 <form:form action="${pageContext.request.contextPath }/empUpdate" 
method="POST" modelAttribute="employee">                        
<%--
         LastName : <form:input path="lastName" /><br><br>
          --%>
                 <form:hidden path="id"/>
                 <input type="hidden" name="_method" value="PUT">
                 <%-- 这里不要使用form:hidden标签,否则会报错。 
<form:hidden path="_method" value="PUT"/>
                         Spring的隐含标签,没有value属性,同时,path指定的值,在模型对象中也没有这个属性(_method),所以回显时会报错。
                         org.springframework.beans.NotReadablePropertyException: 
                         Invalid property '_method' of bean class [com.atguigu.springmvc.crud.entities.Employee]: 
                         Bean property '_method' is not readable or has an invalid getter method: 
                         Does the return type of the getter match the parameter type of the setter?
                  --%>
                          
         Email : <form:input path="email" /><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
         Gender : <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br><br>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
                 <input type="submit" value="Submit"><br><br>
 </form:form>
</body>
</html>
7.8.2 提交表单,修改数据

1)控制器方法

@RequestMapping(value="/empUpdate",method=RequestMethod.PUT)
public String empUpdate(Employee employee){
employeeDao.save(employee);              
return "redirect:/empList";
}

第8章 处理JSON

8.1 返回JSON

1)加入 jar 包:
http://wiki.fasterxml.com/JacksonDownload/ 下载地址
jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar
2)编写目标方法,使其返回 JSON 对应的对象或集合

@ResponseBody  //SpringMVC对JSON的支持,负责将方法的返回值转化成json字符串,响应给浏览器
@RequestMapping("/testJSON")
public Collection<Employee> testJSON(){
return employeeDao.getAll();
}

3)增加页面代码:index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){ 
$("#testJSON").click(function(){
 
var url = this.href ;
var args = {};
$.post(url,args,function(data){
for(var i=0; i<data.length; i++){
var id = data[i].id;
var lastName = data[i].lastName ;
alert(id+" - " + lastName);
}
});
 
return false ;
});                
});
</script>
 
</head>
<body>
 
<a href="empList">To Employee List</a>
<br><br>
 
<a id="testJSON" href="testJSON">testJSON</a>
 
</body>
</html>

4)测试

在这里插入图片描述

8.2 HttpMessageConverter原理

8.2.1 HttpMessageConverter

1)HttpMessageConverter 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息
2)HttpMessageConverter接口定义的方法:
①Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)
②Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义。
③List getSupportMediaTypes():该转换器支持的媒体类型。
④T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。
⑤void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类型为 contentType。

在这里插入图片描述

package org.springframework.http;
 
import java.io.IOException;
import java.io.InputStream;
 
public interface HttpInputMessage extends HttpMessage {
 
InputStream getBody() throws IOException;
 
}	
package org.springframework.http;
 
import java.io.IOException;
import java.io.OutputStream;
 
public interface HttpOutputMessage extends HttpMessage {
 
OutputStream getBody() throws IOException;
 
}

在这里插入图片描述

3)DispatcherServlet 默认装配 RequestMappingHandlerAdapter ,
而 RequestMappingHandlerAdapter 默认装配如下 HttpMessageConverter:

在这里插入图片描述

4)加入 jackson jar 包后, RequestMappingHandlerAdapter
装配的 HttpMessageConverter 如下:

在这里插入图片描述

默认情况下数组长度是6个;增加了jackson的包,后多个一个

MappingJackson2HttpMessageConverter

在这里插入图片描述

8.3使用HttpMessageConveter完成下载功能

/**
	 * 使用HttpMessageConveter完成下载功能:
	 * 
	 * 支持  @RequestBody   @ResponseBody   HttpEntity  ResponseEntity
	 * 
	 * 下载的原理:  将服务器端的文件 以流的形式  写到 客户端. 
	 * ResponseEntity: 将要下载的文件数据, 以及响应信息封装到ResponseEntity对象中,浏览器端通过解析
	 * 				       发送回去的响应数据, 就可以进行一个下载操作. 
	 */
	@RequestMapping("/download")
	public ResponseEntity<byte[]> testDownload(HttpSession session ) throws Exception{
		//将要下载的文件读取成一个字节数据
		byte [] imgs ;
		
		ServletContext sc = session.getServletContext();
		
		InputStream in = sc.getResourceAsStream("image/songlaoshi.jpg");
		
		imgs = new byte[in.available()] ; 
		
		in.read(imgs);
		
		//将响应数据  以及一些响应头信息封装到ResponseEntity中
		/*
		 * 参数:
		 * 	1. 发送给浏览器端的数据
		 *  2. 设置响应头
		 *  3. 设置响应码
		 */
		HttpHeaders  headers = new HttpHeaders();
		headers.add("Content-Disposition", "attachment;filename=songlaoshi.jpg");
		
		HttpStatus statusCode = HttpStatus.OK;  // 200
		
		ResponseEntity<byte[]>  re = new ResponseEntity<byte[]>(imgs, headers, statusCode);
	
		
		return re ; 
	}

<mvc:annotation-driven>的作用:

  1. 配置了<mvc:view-controller>
  2. 配置了<mvc:default-servlet-handler>
  3. 处理Json,对HttpMessageConveter的支持
  4. 对数据绑定流程的支持 , 对异常处理的支持等 <mvc:annotation-driven>的原理: 启动一些新的组件对象替换原先旧的组件对象, 从而实现一些新的,更强大的功能

解释: <mvc:default-servlet-handler>① 为什么还要配置<mvc:annotation-driven> ②?

① ② 都没有配置的情况: DispatcherServlet 中handlerAdapters中装配:
HttpRequestHandlerAdapter
SimpleControlleraHandlerAdapter
AnnotationMethodHandlerAdapter

① 配置 ②不配置的情况 :DispatcherServlet 中handlerAdapters中装配: HttpRequestHandlerAdapter
SimpleControlleraHandlerAdapter
① ② 都配置的情况: DispatcherServlet 中handlerAdapters中装配: HttpRequestHandlerAdapter
SimpleControlleraHandlerAdapter
RequestMappingHandlerAdapter

  总结:  在Spring 3.2 之后  ,RequestMappingHandlerAdapter 替换掉了AnnotationMethodHandlerAdapter。

第9章 文件上传

9.1 文件上传

1)Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。
2)Spring 用 Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResolver
3)Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver

在这里插入图片描述

4)配置 MultipartResolver
defaultEncoding: 必须和用户 JSP 的 pageEncoding 属性一致,以便正确解析表单的内容,为了让 CommonsMultipartResolver 正确工作,必须先将 Jakarta Commons FileUpload 及 Jakarta Commons io 的类包添加到类路径下。

在这里插入图片描述

9.2 文件上传示例

1)拷贝jar包

commons-fileupload-1.2.1.jar
commons-io-2.0.jar
严重: Servlet /SpringMVC_06_FileUpload threw load() exception
java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileItemFactory

2)配置文件上传解析器

<!-- 配置文件上传解析器
id必须是"multipartResolver",否则,会报错误:
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
 -->
<bean id="multipartResolver"
 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024000"></property>
</bean>

3)上传页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="testUpload" method="post" enctype="multipart/form-data">
文件: <input type="file" name="file"/><br><br>
描述: <input type="text" name="desc"/><br><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>

4)控制器方法

package com.atguigu.springmvc.crud.handlers;
 
import java.io.IOException;
import java.io.InputStream;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
 
@Controller
public class UploadHandler {
 
@RequestMapping(value="/testUpload",method=RequestMethod.POST)
public String testUpload(@RequestParam(value="desc",required=false) String desc, @RequestParam("file") MultipartFile multipartFile) throws IOException{                
System.out.println("desc : "+desc);
System.out.println("OriginalFilename : "+multipartFile.getOriginalFilename());
InputStream inputStream = multipartFile.getInputStream();
System.out.println("inputStream.available() : "+inputStream.available());
System.out.println("inputStream : "+inputStream);

return "success"; //增加成功页面: /views/success.jsp
}
}

9.3 思考多个文件上传?

在这里插入图片描述

第10章 拦截器

10.1 自定义拦截器概述

1)Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器可以实现HandlerInterceptor接口,或者可以继承HandlerInterceptorAdapter 适配器类
preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

10.2 实验代码(单个拦截器)

1)自定义拦截器类

package com.atguigu.springmvc.interceptors;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class FirstHandlerInterceptor implements HandlerInterceptor {
 
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
System.out.println(this.getClass().getName() + " - afterCompletion");
}
 
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
System.out.println(this.getClass().getName() + " - postHandle");
}
 
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2) throws Exception {
System.out.println(this.getClass().getName() + " - preHandle");
return true;
} 
}

2)配置拦截器

<mvc:interceptors>
<!-- 声明自定义拦截器 -->
<bean id="firstHandlerInterceptor"
      class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>
</mvc:interceptors>

3)断点调试拦截器执行流程

在这里插入图片描述

4)拦截器方法执行顺序(小总结)

在这里插入图片描述

10.3 实验代码(多个拦截器)

1)自定义拦截器类(两个)

package com.atguigu.springmvc.interceptors;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class FirstHandlerInterceptor implements HandlerInterceptor {
 
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println(this.getClass().getName() + " - afterCompletion");
}
 
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
System.out.println(this.getClass().getName() + " - postHandle");
}
 
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2) throws Exception {
System.out.println(this.getClass().getName() + " - preHandle");
return true;
}
 
}	
package com.atguigu.springmvc.interceptors;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class SecondHandlerInterceptor implements HandlerInterceptor {
 
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println(this.getClass().getName() + " - afterCompletion");
}
 
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
System.out.println(this.getClass().getName() + " - postHandle");
}
 
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2) throws Exception {
System.out.println(this.getClass().getName() + " - preHandle");
return true;
}
 
}

2)配置自定义拦截器

<mvc:interceptors>
<!-- 声明自定义拦截器 -->
<bean id="firstHandlerInterceptor"
  class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>
<!-- 配置拦截器引用 -->
<mvc:interceptor>                        
<mvc:mapping path="/empList"/>
<!-- <mvc:exclude-mapping path="/empList"/> -->
<bean id="secondHandlerInterceptor"
         class="com.atguigu.springmvc.interceptors.SecondHandlerInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
两个都是返回true :
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - preHandle
************************************biz method*******************************
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - postHandle
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - postHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - afterCompletion
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion
两个都是返回false:
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
true,false
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - preHandle
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion
false,true 
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle

10.4 多个拦截方法的执行顺序

1)关于执行顺序

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor – preHandle
************************************biz method*******************************
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - postHandle
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - postHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - afterCompletion
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion

2)执行顺序图解
在这里插入图片描述

3)从源代码的执行角度分析流程:
PreHandle从小往大迭代

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (getInterceptors() != null) {
for (int i = 0; i < getInterceptors().length; i++) {
	HandlerInterceptor interceptor = getInterceptors()[i];
	if (!interceptor.preHandle(request, response, this.handler)) {
		triggerAfterCompletion(request, response, null);
		return false;
}
	this.interceptorIndex = i;
}
}
	return true;
}

在这里插入图片描述
PostHandle从小往大迭代

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
if (getInterceptors() == null) {
	return;
}
for (int i = getInterceptors().length - 1; i >= 0; i--) {
	HandlerInterceptor interceptor = getInterceptors()[i];
	interceptor.postHandle(request, response, this.handler, mv);
}
}

在这里插入图片描述

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
 
if (getInterceptors() == null) {
return;
}
for (int i = this.interceptorIndex; i >= 0; i--) {
	HandlerInterceptor interceptor = getInterceptors()[i];
	try {
		interceptor.afterCompletion(request, response, this.handler, ex);
	}
	catch (Throwable ex2) {
		logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}

在这里插入图片描述

在这里插入图片描述

4)源码分析:分析interceptorIndex的值情况

在这里插入图片描述
preHandle postHandle afterCompletion执行策略

package com.atguigu.springmvc.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;


/**
 * 自定义拦截器
 */
//@Component
public class MyFirstInterceptor implements HandlerInterceptor {
	/**
	 * 1. 是在DispatcherServlet的939行   在请求处理方法之前执行
	 */
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("MyFirstInterceptor  preHandle");
		return true;
	}
	/**
	 * 2. 在DispatcherServlet 959行   请求处理方法之后,视图处理之前执行。
	 */
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("MyFirstInterceptor postHandle");
	}
	/**
	 * 3. 
	 * 	 [1].在DispatcherServlet的 1030行   视图处理之后执行.(转发/重定向后执行)
	 * 	 [2].当某个拦截器的preHandle返回false后,也会执行当前拦截器之前拦截器的afterCompletion
	 *   [3].当DispatcherServlet的doDispath方法抛出异常,也可能会执行拦截器的afterComletion
	 */
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("MyFirstInterceptor afterCompletion");
	}

}

第11章 运行流程图解

11.1 流程图

在这里插入图片描述

11.2 Spring工作流程描述

1)用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获;
2)DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI):
判断请求URI对应的映射

①不存在:

再判断是否配置了mvc:default-servlet-handler:
如果没配置,则控制台报映射查找不到,客户端展示404错误
如果有配置,则执行目标资源(一般为静态资源,如:JS,CSS,HTML)

②存在:
执行下面流程
3)根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
4)DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
5)如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法【正向】
6)提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
①HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
②数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
③数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
④数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
7)Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
8)此时将开始执行拦截器的postHandle(…)方法【逆向】
9)根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet,根据Model和View,来渲染视图
10)在返回给客户端时需要执行拦截器的AfterCompletion方法【逆向】
11)将渲染结果返回给客户端

11.3 源码解析

11.3.1 搭建环境

1)拷贝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

<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>

3)配置文件springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 设置扫描组件的包 -->
<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>
 
</beans>
11.3.2 完成HelloWorld

1)页面链接

<a href="helloworld">Hello World</a>

2)控制器方法

package com.atguigu.springmvc.handler;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
@Controller
public class HelloWorldHandler { 
@RequestMapping("/helloworld")
public String testHello(){ 
System.out.println("Hello,SpringMVC..."); 
return "success";
} 
}

3)成功页面:

/views/success.jsp
11.3.3 Debug实验

1)正常流程,运行出结果

2)没有配置<mvc:default-servlet-handler/>,测试,直接报404
①http://localhost:8080/SpringMVC_09_WorkFlow/helloworld2
四月 20, 2016 11:53:19 上午 org.springframework.web.servlet.PageNotFound noHandlerFound
警告: No mapping found for HTTP request with URI [/SpringMVC_09_WorkFlow/helloworld2] in DispatcherServlet with name 'springDispatcherServlet'
②http://localhost:8080/SpringMVC_09_WorkFlow/test.html
四月 20, 2016 11:54:16 上午 org.springframework.web.servlet.PageNotFound noHandlerFound
警告: No mapping found for HTTP request with URI [/SpringMVC_09_WorkFlow/test.html] in DispatcherServlet with name 'springDispatcherServlet'
3)配置<mvc:default-servlet-handler/>,测试,会去查找目标资源
4)测试,依然发生错误,这时,需要配置:<mvc:annotation-driven/>,否则,映射解析不好使。

在这里插入图片描述

11.3.4 Debug流程分析

1)HandlerExecutionChain mappedHandler;包含了拦截器和处理器方法;
DispatcherServlet L902 916

org.springframework.web.servlet.HandlerExecutionChain
Handler execution chain, consisting of handler object and any handler interceptors. Returned by
 HandlerMapping's HandlerMapping.getHandler method.

在这里插入图片描述

2)HandlerMapping

org.springframework.web.servlet.HandlerMapping
Interface to be implemented by objects that define a mapping between requests and handler objects. 
This class can be implemented by application developers, although this is not necessary, as org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping and org.springframework.web.servlet.handler.SimpleUrlHandlerMapping are included in the framework. The former is the default if no HandlerMapping bean is registered in the application context. 
HandlerMapping implementations can support mapped interceptors but do not have to. A handler will always be wrapped in a HandlerExecutionChain instance, optionally accompanied by some HandlerInterceptor instances. The DispatcherServlet will first call each HandlerInterceptor's preHandle method in the given order, finally invoking the handler itself if all preHandle methods have returned true. 
The ability to parameterize this mapping is a powerful and unusual capability of this MVC framework. For example, it is possible to write a custom mapping based on session state, cookie state or many other variables. No other MVC framework seems to be equally flexible. 
Note: Implementations can implement the org.springframework.core.Ordered interface to be able to specify a sorting order and thus a priority for getting applied by DispatcherServlet. Non-Ordered instances get treated as lowest priority.
3)没有配置<mvc:default-servlet-handler/>,<mvc:annotation-driven/>,发送一个不存在资源的请求路径,mappedHandler为null
http://localhost:8080/SpringMVC_09_WorkFlow/helloworld2

在这里插入图片描述

在这里插入图片描述

4)配置<mvc:default-servlet-handler/>,<mvc:annotation-driven/>,发送一个不存在资源的请求路径
http://localhost:8080/SpringMVC_09_WorkFlow/helloworld2
mappedHandler不为null,原因是当循环simpleUrlHandlerMapping时,当做静态资源处理

在这里插入图片描述

11.3.5 断点

断点

第12章 Spring整合SpringMVC

通常情况下, 类似于数据源, 事务, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中).
实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao. 不需要都放在 SpringMVC的配置文件中. 也可以分多个 Spring 的配置文件, 然后使用 import 节点导入其他的配置文件

12.1 Spring 与SpringMVC的整合问题:

1)需要进行 Spring 整合 SpringMVC 吗 ?
2)还是否需要再加入 Spring 的 IOC 容器 ?
3)是否需要在web.xml 文件中配置启动 Spring IOC 容器的 ContextLoaderListener ?

需要: 通常情况下, 类似于数据源, 事务, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中).
实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao.
不需要: 都放在 SpringMVC 的配置文件中. 也可以分多个 Spring 的配置文件, 然后使
用 import 节点导入其他的配置文件
4)如何启动Spring IOC容器?
非WEB环境:直接在main方法或者是junit测试方法中通过new操作来创建.
WEB 环境: 我们希望SpringIOC容器在WEB应用服务器启动时就被创建.
通过监听器来监听ServletContext对象的创建, 监听到ServletContext对象被创建,就创建SpringIOC容器。
并且将容器对象绑定到ServletContext中, 让所有的web组件能共享到IOC容器对象.

12.2 Spring整合SpringMVC_解决方案配置监听器

1)监听器配置

<!-- 配置启动 Spring IOC 容器的 Listener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2)创建Spring的bean的配置文件:beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 设置扫描组件的包 -->
<context:component-scan base-package="com.atguigu.springmvc"/>
 
<!-- 配置数据源, 整合其他框架, 事务等. -->
 
</beans>

3)springmvc配置文件:springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 设置扫描组件的包 -->
<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>
 
<mvc:default-servlet-handler/> 
<mvc:annotation-driven/> 
</beans>

在HelloWorldHandler、UserService类中增加构造方法,启动服务器,查看构造器执行情况。
问题: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.
解决:
使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分.
使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解
spring.xml中指定扫描,applicationContext.xml中配置排除扫描
Spring用来管理Dao和Service(Dao注入到Service),SpringMVC用来管理Handler,Handler调用Service

springmvc.xml
<context:component-scan base-package="com.atguigu.springmvc" use-default-filters="false">
<context:include-filter type="annotation"
           expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
           expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
beans.xml
<context:component-scan base-package="com.atguigu.springmvc">
<context:exclude-filter type="annotation"
        expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
        expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 配置数据源, 整合其他框架, 事务等. -->

12.3 SpringIOC 容器和 SpringMVC IOC 容器的关系

SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean.
返回来呢 ? 反之则不行. Spring IOC 容器中的 bean 却不能来引用 SpringMVC IOC 容器中的 bean
1)在 Spring MVC 配置文件中引用业务层的 Bean
2)多个 Spring IOC 容器之间可以设置为父子关系,以实现良好的解耦。
3)Spring MVC WEB 层容器可作为 “业务层” Spring 容器的子容器:
即 WEB 层容器可以引用业务层容器的 Bean,而业务层容器却访问不到 WEB 层容器的 Bean

IOC容器 和 MVC容器 是 父子关系,子可以调用父,而不能反过来调用
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值