目录
4.1、使用ServletAPI向request域对象共享数据
4.2、使用ModelAndView向request域对象共享数据
11.2、创建SpringConfig配置类,代替spring的配置文件
11.3、创建WebConfig配置类,代替SpringMVC的配置文件
12.3、DispatcherServlet调用组件处理请求
大部分笔记来自尚硅谷springmvc学习笔记,学习课程链接springmvc网课
1.SpringMVC简介
1.1什么是MVC
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
- Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
- View(视图) - 视图代表模型包含的数据的可视化。
- Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
MVC
的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被
Controller
接收,
Controller
调用相应的
Model
层处理请求,处理完毕将结果返回到
Controller
,
Controller
再根据请求处理的结果
找到相应的
View
视图,渲染数据后最终响应给浏览器
1.2、什么是SpringMVC
SpringMVC
是
Spring
的一个后续产品,是
Spring
的一个子项目
SpringMVC
是
Spring
为表述层开发提供的一整套完备的解决方案。在表述层框架历经
Strust
、
WebWork
、
Strust2
等诸多产品的历代更迭之后,目前业界普遍选择了
SpringMVC
作为
Java EE
项目
表述层开发的
首选方案
。
注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台
servlet
1.3、SpringMVC的特点
Spring
家族原生产品
,与
IOC
容器等基础设施无缝对接
基于原生的
Servlet
,通过了功能强大的
前端控制器
DispatcherServlet
,对请求和响应进行统一
处理
表述层各细分领域需要解决的问题
全方位覆盖
,提供
全面解决方案
代码清新简洁
,大幅度提升开发效率
内部组件化程度高,可插拔式组件
即插即用
,想要什么功能配置相应组件即可
性能卓著
,尤其适合现代大型、超大型互联网项目要求
2.快速入门
2.1环境准备
创建
maven
工程
a>
添加
web
模块
b>
打包方式:
war
c>
引入依赖
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
2.2、配置web.xml
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass>
<!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
<init-param>
<!-- contextConfigLocation为固定值 -->
<param-name>contextConfigLocation</param-name>
<!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的
src/main/resources -->
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--
作为框架的核心组件,在启动过程中有大量的初始化操作要做
而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!--
设置springMVC的核心控制器所能处理的请求的请求路径
/所匹配的请求可以是/login或.html或.js或.css方式的请求路径
但是/不能匹配.jsp请求路径的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
注:
<url-pattern>
标签中使用
/
和
/*
的区别:
/
所匹配的请求可以是
/login
或
.html
或
.js
或
.css
方式的请求路径,但是
/
不能匹配
.jsp
请求路径的请
求
因此就可以避免在访问
jsp
页面时,该请求被
DispatcherServlet
处理,从而找不到相应的页面
/*
则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用
/*
的写
法
2.3、创建请求控制器
由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要
创建处理具体请求的类,即请求控制器
请求控制器中每一个处理请求的方法成为控制器方法
因为
SpringMVC
的控制器由一个
POJO
(普通的
Java
类)担任,因此需要通过
@Controller
注解将其标识
为一个控制层组件,交给
Spring
的
IoC
容器管理,此时
SpringMVC
才能够识别控制器的存在
@Controller
public class HelloController {
}
2.4、创建springMVC的配置文件
<!-- 自动扫描包 -->
<context:component-scan base-package="com.atguigu.mvc.controller"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean></property>
</bean>
<!--
处理静态资源,例如html、js、css、jpg
若只设置该标签,则只能访问静态资源,其他请求则无法访问
此时必须设置<mvc:annotation-driven/>解决问题
-->
<mvc:default-servlet-handler/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 处理响应中文内容乱码 -->
<bean
class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
2.5测试HelloWorld
在请求控制器中创建处理请求的方法
@RequestMapping("/")
public String index() {
//设置视图名称
return "index";
}
浏览器发送请求,若请求地址符合前端控制器的
url-pattern
,该请求就会被前端控制器
DispatcherServlet
处理。前端控制器会读取
SpringMVC
的核心配置文件,通过扫描组件找到控制器。将请求地址和控制器中
@RequestMapping
注解的
value
属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过
Thymeleaf
对视图进行渲染,最终转发到视
图所对应页面
2.6、@RequestMapping注解
2.6.1、@RequestMapping注解的功能
从注解名称上我们可以看到,
@RequestMapping
注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
SpringMVC
接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
2.6.2、@RequestMapping注解的位置
@RequestMapping
标识一个类:设置映射请求的请求路径的初始信息
@RequestMapping
标识一个方法:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/test")
public class RequestMappingController {
//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
@RequestMapping("/testRequestMapping")
public String testRequestMapping(){
return "success";
}
}
2.6.3、@RequestMapping注解的属性
value属性:
@RequestMapping
注解的
value
属性通过请求的请求地址匹配请求映射
@RequestMapping
注解的
value
属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
@RequestMapping
注解的
value
属性必须设置,至少通过请求地址匹配请求映射
method属性:
@RequestMapping
注解的
method
属性通过请求的请求方式(
get
或
post
)匹配请求映射
@RequestMapping
注解的
method
属性是一个
RequestMethod
类型的数组,表示该请求映射能够匹配多种请求方式的请求
若当前请求的请求地址满足请求映射的
value
属性,但是请求方式不满足
method
属性,则浏览器报错
405
:
Request method 'POST' not supported
@RequestMapping(
value = {"/testRequestMapping", "/test"},
method = {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){
return "success";
}
注:
1
、对于处理指定请求方式的控制器方法,
SpringMVC
中提供了
@RequestMapping
的派生注解
处理
get
请求的映射
-->@GetMapping
处理
post
请求的映射
-->@PostMapping
处理
put
请求的映射
-->@PutMapping
处理
delete
请求的映射
-->@DeleteMapping
2
、常用的请求方式有
get
,
post
,
put
,
delete
但是目前浏览器只支持
get
和
post
,若在
form
表单提交时,为
method
设置了其他请求方式的字符
串(
put
或
delete
),则按照默认的请求方式
get
处理,若要发送
put
和
delete
请求,则需要通过
spring
提供的过滤器
HiddenHttpMethodFilter
params属性:
@RequestMapping
注解的
params
属性通过请求的请求参数匹配请求映射
@RequestMapping
注解的
params
属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
"param"
:要求请求映射所匹配的请求必须携带
param
请求参数
"!param"
:要求请求映射所匹配的请求必须不能携带
param
请求参数
"param=value"
:要求请求映射所匹配的请求必须携带
param
请求参数且
param=value
"param!=value"
:要求请求映射所匹配的请求必须携带
param
请求参数但是
param!=value
若当前请求满足
@RequestMapping
注解的
value
和
method
属性,但是不满足
params
属性,此时
页面回报错
400
headers
属性:
@RequestMapping
注解的
headers
属性通过请求的请求头信息匹配请求映射
@RequestMapping
注解的
headers
属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
"header"
:要求请求映射所匹配的请求必须携带
header
请求头信息
"!header"
:要求请求映射所匹配的请求必须不能携带
header
请求头信息
"header=value"
:要求请求映射所匹配的请求必须携带
header
请求头信息且
header=value
"header!=value"
:要求请求映射所匹配的请求必须携带
header
请求头信息且
header!=value
若当前请求满足
@RequestMapping
注解的
value
和
method
属性,但是不满足
headers
属性,此时页面显示
404
错误,即资源未找到
2.6.4、SpringMVC支持ant风格的路径
?:表示任意的单个字符
*
:表示任意的
0
个或多个字符
**
:表示任意的一层或多层目录
注意:在使用
**
时,只能使用
/**/xxx
的方式
2.6.5、SpringMVC支持路径中的占位符
原始方式:
/deleteUser?id=1
rest
方式:
/deleteUser/1
SpringMVC
路径中的占位符常用于
RESTful
风格中,当请求路径中将某些数据通过路径的方式传输到服
务器中,就可以在相应的
@RequestMapping
注解的
value
属性中通过占位符
{xxx}
表示传输的数据,在
通过
@PathVariable
注解,将占位符所表示的数据赋值给控制器方法的形参。
<a th:href="@{/testRest/1/admin}">测试路径中的占位符-->/testRest</a><br>
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username")
String username){
System.out.println("id:"+id+",username:"+username);
return "success";
}
//最终输出的内容为-->id:1,username:admin
3、SpringMVC获取请求参数
3.1、通过ServletAPI获取
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}
3.2、通过控制器方法的形参获取请求参数
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在
DispatcherServlet
中就会将请求参数赋值给相应的形参。
<a th:href="@{/testParam(username='admin',password=123456)}">测试获取请求参数--
>/testParam</a><br>
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
注:
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串
数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
3.3、@RequestParam
@RequestParam
是将请求参数和控制器方法的形参创建映射关系
@RequestParam
注解一共有三个属性:
value
:指定为形参赋值的请求参数的参数名
required
:设置是否必须传输此请求参数,默认值为
true
若设置为
true
时,则当前请求必须传输
value
所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue
属性,则页面报错
400
:
Required String parameter 'xxx' is not present
;若设置为
false
,则当前请求不是必须传输
value
所指定的请求参数,若没有传输,则注解所标识的形参的值为null
defaultValue
:不管
required
属性值为
true
或
false
,当
value
所指定的请求参数没有传输或传输的值
为
""
时,则使用默认值为形参赋值
3.4、@RequestHeader
@RequestHeader
是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader
注解一共有三个属性:
value
、
required
、
defaultValue
,用法同
@RequestParam
3.5、@CookieValue
@CookieValue
是将
cookie
数据和控制器方法的形参创建映射关系
@CookieValue
注解一共有三个属性:
value
、
required
、
defaultValue
,用法同
@RequestParam
3.6、通过POJO获取请求参数
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
<form th:action="@{/testpojo}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男">男<input type="radio"
name="sex" value="女">女<br>
年龄:<input type="text" name="age"><br>
邮箱:<input type="text" name="email"><br>
<input type="submit">
</form>
@RequestMapping("/testpojo")
public String testPOJO(User user){
System.out.println(user);
return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男',
email='123@qq.com'}
3.7、解决获取请求参数的乱码问题
解决获取请求参数的乱码问题,可以使用
SpringMVC
提供的编码过滤器
CharacterEncodingFilter
,但是必须在
web.xml
中进行注册
<!--配置springMVC的编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filterclass>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>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4、域对象共享数据
4.1、使用ServletAPI向request域对象共享数据
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
4.2、使用ModelAndView向request域对象共享数据
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
/**
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域共享数据
mav.addObject("testScope", "hello,ModelAndView");
//设置视图,实现页面跳转
mav.setViewName("success");
return mav;
}
4.3、使用Model向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testScope", "hello,Model");
return "success";
}
4.4、使用map向request域对象共享数据
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
map.put("testScope", "hello,Map");
return "success";
}
4.5、使用ModelMap向request域对象共享数据
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testScope", "hello,ModelMap");
return "success";
}
4.6、Model、ModelMap、Map的关系
Model
、
ModelMap
、
Map
类型的参数其实本质上都是
BindingAwareModelMap
类型的
public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
4.7、向session域共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
4.8、向application域共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("testApplicationScope", "hello,application");
return "success";
}
5、SpringMVC的视图
SpringMVC
中的视图是
View
接口,视图的作用渲染数据,将模型
Model
中的数据展示给用户
SpringMVC
视图的种类很多,默认有转发视图和重定向视图
当工程引入
jstl
的依赖,转发视图会自动转换为
JstlView
若使用的视图技术为
Thymeleaf
,在
SpringMVC
的配置文件中配置了
Thymeleaf
的视图解析器,由此视
图解析器解析之后所得到的是
ThymeleafView
5.1、ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被
SpringMVC
配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。
@RequestMapping("/testHello")
public String testHello(){
return "hello";
}
5.2、转发视图
SpringMVC
中默认的转发视图是
InternalResourceView
SpringMVC
中创建转发视图的情况:当控制器方法中所设置的视图名称以
"forward:"
为前缀时,创建
InternalResourceView
视图,此时的视图名称不会被
SpringMVC
配置文件中所配置的视图解析器解析,而是会将前缀
"forward:"
去掉,剩余部分作为最终路径通过转发的方式实现跳转
例如
"forward:/"
,
"forward:/employee"
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testHello";
}
5.3、重定向视图
SpringMVC
中默认的重定向视图是
RedirectView
当控制器方法中所设置的视图名称以
"redirect:"
为前缀时,创建
RedirectView
视图,此时的视图名称不会被
SpringMVC
配置文件中所配置的视图解析器解析,而是会将前缀
"redirect:"
去掉,剩余部分作为最
终路径通过重定向的方式实现跳转
例如
"redirect:/"
,
"redirect:/employee"
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/testHello";
}
5.4、视图控制器view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用
view
controller
标签进行表示
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
注:
当
SpringMVC
中设置任何一个
view-controller
时,其他控制器中的请求映射将全部失效,此时需
要在
SpringMVC
的核心配置文件中设置开启
mvc
注解驱动的标签:
<mvc:annotation-driven />
6、RESTful
6.1、RESTful简介
REST
:
Re
presentational
S
tate
T
ransfer
,表现层资源状态转移。
资源 :
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、
数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端
应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个
资源可以由一个或多个
URI
来标识。
URI
既是资源的名称,也是资源在
Web
上的地址。对某个资源感兴
趣的客户端应用,可以通过资源的
URI
与其进行交互。
资源的表述 :
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端
-
服务器端之间转移(交
换)。资源的表述可以有多种格式,例如
HTML/XML/JSON/
纯文本
/
图片
/
视频
/
音频等等。资源的表述格式可以通过协商机制来确定。请求
-
响应方向的表述通常使用不同的格式。
状态转移 :
状态转移说的是:在客户端和服务器端之间转移(
transfer
)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
6.2、RESTful的实现
具体说,就是
HTTP
协议里面,四个表示操作方式的动词:
GET
、
POST
、
PUT
、
DELETE
。
它们分别对应四种基本操作:
GET
用来获取资源,
POST
用来新建资源,
PUT
用来更新资源,
DELETE用来删除资源。
REST
风格提倡
URL
地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方
式携带请求参数,而是将要发送给服务器的数据作为
URL
地址的一部分,以保证整体风格的一致性。
![](https://i-blog.csdnimg.cn/blog_migrate/b388f11caf00ce43b9467811ba498572.png)
6. 3、HiddenHttpMethodFilter
由于浏览器只支持发送
get
和
post
方式的请求,那么该如何发送
put
和
delete
请求呢?
SpringMVC
提供了
HiddenHttpMethodFilter
帮助我们
将
POST
请求转换为
DELETE
或
PUT
请求HiddenHttpMethodFilter
处理
put
和
delete
请求的条件:
a>
当前请求的请求方式必须为
post
b>
当前请求必须传输请求参数
_method
满足以上条件,
HiddenHttpMethodFilter
过滤器就会将当前请求的请求方式转换为请求参数
_method
的值,因此请求参数
_method
的值才是最终的请求方式
在
web.xml
中注册
HiddenHttpMethodFilter
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注:
目前为止,
SpringMVC
中提供了两个过滤器:
CharacterEncodingFilter
和HiddenHttpMethodFilter在
web.xml
中注册时,必须先注册
CharacterEncodingFilter
,再注册
HiddenHttpMethodFilter
原因:
在
CharacterEncodingFilter
中通过
request.setCharacterEncoding(encoding)
方法设置字
符集的
request.setCharacterEncoding(encoding)
方法要求前面不能有任何获取请求参数的操作
而
HiddenHttpMethodFilter
恰恰有一个获取请求方式的操作:
String paramValue = request.getParameter(this.methodParam);
7、HttpMessageConverter
HttpMessageConverter
,报文信息转换器,将请求报文转换为
Java对象,或将Java对象转换为响应报文 HttpMessageConverter提供了两个注解和两个类型:
@RequestBody
,
@ResponseBody
, RequestEntity
,
ResponseEntity
7.1、@RequestBody
@RequestBody
可以获取请求体,需要在控制器方法设置一个形参,使用
@RequestBody
进行标识,当前请求的请求体就会为当前注解所标识的形参赋值
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
输出结果:
requestBody:username=admin&password=123456
7.2、RequestEntity
RequestEntity
封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过
getHeaders()
获取请求头信息,通过
getBody()
获取请求体信息
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
System.out.println("requestHeader:"+requestEntity.getHeaders());
System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
7.3、@ResponseBody
@ResponseBody
用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
结果:浏览器页面显示
success
7.4、SpringMVC处理json
@ResponseBody
处理
json
的步骤:
1、
导入
jackson
的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
2、
在
SpringMVC
的核心配置文件中开启
mvc
的注解驱动,此时在
HandlerAdaptor
中会自动装配一个消息转换器:
MappingJackson2HttpMessageConverter
,可以将响应到浏览器的
Java
对象转换为
Json
格式的字符串 <mvc:annotation-driven />
3
、在处理器方法上使用
@ResponseBody
注解进行标识
4、
将
Java
对象直接作为控制器方法的返回值返回,就会自动转换为
Json
格式的字符串
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
return new User(1001,"admin","123456",23,"男");
}
浏览器的页面中展示的结果:
{"id":1001,"username":"admin","password":"123456","age":23,"sex":"
男
"}
7.5、SpringMVC处理ajax
1、
请求超链接:
<div id="app">
<a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>
2、通过
vue
和
axios
处理点击事件:
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
var vue = new Vue({
el:"#app",
methods:{
testAjax:function (event) {
axios({
method:"post",
url:event.target.href,
params:{
username:"admin",
password:"123456"
}
}).then(function (response) {
alert(response.data);
});
event.preventDefault();
}
}
});
</script>
3、控制器方法:
@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "hello,ajax";
}
7.6、@RestController注解
@RestController
注解是
springMVC
提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller
注解,并且为其中的每个方法添加了
@ResponseBody
注解 。
7.7、ResponseEntity
ResponseEntity
用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文 。
8、文件上传和下载
8.1、文件下载
使用
ResponseEntity
实现下载文件的功能
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws
IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers,
statusCode);
//关闭输入流
is.close();
return responseEntity;
}
8.2、文件上传
文件上传要求
form
表单的请求方式必须为
post
,并且添加属性
enctype="multipart/form-data"
SpringMVC
中将上传的文件封装到
MultipartFile
对象中,通过此对象可以获取文件相关信息
上传步骤:
1、
添加依赖:
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --
>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
2、
在
SpringMVC
的配置文件中添加配置:
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
3、
控制器方法:
@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws
IOException {
//获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//处理文件重名问题
String hzName = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID().toString() + hzName;
//获取服务器中photo目录的路径
ServletContext servletContext = session.getServletContext();
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
if(!file.exists()){
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
//实现上传功能
photo.transferTo(new File(finalPath));
return "success";
}
9、拦截器
9.1、拦截器的配置
SpringMVC
中的拦截器用于拦截控制器方法的执行
SpringMVC
中的拦截器需要实现
HandlerInterceptor
SpringMVC
的拦截器必须在
SpringMVC
的配置文件中进行配置:
<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/testRequestEntity"/>
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!--
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过
mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
9..2、拦截器的三个抽象方法
SpringMVC
中的拦截器有三个抽象方法:
preHandle
:控制器方法执行之前执行
preHandle()
,其
boolean
类型的返回值表示是否拦截或放行,返
回
true
为放行,即调用控制器方法;返回
false
表示拦截,即不调用控制器方法
postHandle
:控制器方法执行之后执行
postHandle()
afterComplation
:处理完视图和模型数据,渲染视图完毕之后执行
afterComplation()
9.3、多个拦截器的执行顺序
1、
若每个拦截器的
preHandle()
都返回
true
此时多个拦截器的执行顺序和拦截器在
SpringMVC
的配置文件的配置顺序有关:
preHandle()
会按照配置的顺序执行,而
postHandle()
和
afterComplation()
会按照配置的反序执行
2、
若某个拦截器的
preHandle()
返回了false
preHandle()
返回
false
和它之前的拦截器的
preHandle()
都会执行,
postHandle()
都不执行,返回
false的拦截器之前的拦截器的
afterComplation()
会执行
10、异常处理器
10.1、基于配置的异常处理
SpringMVC
提供了一个处理控制器方法执行过程中所出现的异常的接口:
HandlerExceptionResolver
HandlerExceptionResolver
接口的实现类有:
DefaultHandlerExceptionResolver
和
SimpleMappingExceptionResolver
SpringMVC
提供了自定义的异常处理器
SimpleMappingExceptionResolver
,使用方式:
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
10.2、基于注解的异常处理
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
@ExceptionHandler(ArithmeticException.class)
//ex表示当前请求处理中出现的异常对象
public String handleArithmeticException(Exception ex, Model model){
model.addAttribute("ex", ex);
return "error";
}
}
11、注解配置SpringMVC
使用配置类和注解代替
web.xml
和
SpringMVC
配置文件的功能
11.1、创建初始化类,代替web.xml
public class WebInit extends
AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定spring的配置类
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定SpringMVC的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,即url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 添加过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceRequestEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new
HiddenHttpMethodFilter();
return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
}
}
11.2、创建SpringConfig配置类,代替spring的配置文件
@Configuration
public class SpringConfig {
//ssm整合之后,spring的配置信息写在此类中
}
11.3、创建WebConfig配置类,代替SpringMVC的配置文件
@Configuration
//扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//使用默认的servlet处理静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer
configurer) {
configurer.enable();
}
//配置文件上传解析器
@Bean
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
//配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}
//配置视图控制
/*@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}*/
//配置异常映射
/*@Override
public void
configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new
SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException", "error");
//设置异常映射
exceptionResolver.setExceptionMappings(prop);
//设置共享异常信息的键
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}*/
//配置生成模板解析器
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext =
ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过
WebApplicationContext 的方法获得
ServletContextTemplateResolver templateResolver = new
ServletContextTemplateResolver(
webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//生成模板引擎并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver
templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//生成视图解析器并未解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}
11.4、测试功能
@RequestMapping("/")
public String index(){
return "index";
}
12、SpringMVC执行流程
12.1、SpringMVC常用组件
DispatcherServlet
:
前端控制器
,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
HandlerMapping
:
处理器映射器
,不需要工程师开发,由框架提供
作用:根据请求的
url
、
method
等信息查找
Handler,即控制器方法
Handler
:
处理器
,需要工程师开发
作用:在
DispatcherServlet
的控制下
Handler
对具体的用户请求进行处理
HandlerAdapter
:
处理器适配器
,不需要工程师开发,由框架提供
作用:通过
HandlerAdapter
对处理器(控制器方法)进行执行
ViewResolver
:
视图解析器
,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:
ThymeleafView
、
InternalResourceView
、
RedirectView
View
:
视图
作用:将模型数据通过页面展示给用户
12.2、DispatcherServlet初始化过程
DispatcherServlet
本质上是一个
Servlet
,所以天然的遵循
Servlet
的生命周期。所以宏观上是
Servlet生命周期来进行调度。
1、初始化
WebApplicationContext
所在类:
org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac =
(ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services
such as
// setting the parent context, setting the application context
id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit
parent -> set
// the root application context (if any; may be null) as the
parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is
assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
// 创建WebApplicationContext
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with
refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
// 刷新WebApplicationContext
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
// 将IOC容器在应用域共享
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
2、
创建
WebApplicationContext
所在类:
org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext createWebApplicationContext(@Nullable
ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))
{
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" +
getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 通过反射创建 IOC 容器对象
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext)
BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
// 设置父容器
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
3、DispatcherServlet
初始化策略
FrameworkServlet
创建
WebApplicationContext
后,刷新容器,调用
onRefresh(wac)
,此方法在
DispatcherServlet
中进行了重写,调用了
initStrategies(context)
方法,初始化策略,即初始化
DispatcherServlet
的各个组件
所在类:
org.springframework.web.servlet.DispatcherServlet
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
12.3、DispatcherServlet调用组件处理请求
1、processRequest()
FrameworkServlet
重写
HttpServlet
中的
service()
和
doXxx()
,这些方法中调用了
processRequest(request, response)
所在类:
org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext =
LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes =
RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request,
response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),
new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
2、doService()
所在类:
org.springframework.web.servlet.DispatcherServlet
@Override
protected void doService(HttpServletRequest request, HttpServletResponse
response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude ||
attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName,
request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,
getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request,
response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,
Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath requestPath = null;
if (this.parseRequestPath &&
!ServletRequestPathUtils.hasParsedRequestPath(request)) {
requestPath = ServletRequestPathUtils.parseAndCache(request);
}
try {
// 处理请求和响应
doDispatch(request, response);
}
finally {
if
(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
if (requestPath != null) {
ServletRequestPathUtils.clearParsedRequestPath(request);
}
}
}
3、doDispatch()
所在类:
org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse
response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
/*
mappedHandler:调用链
包含handler、interceptorList、interceptorIndex
handler:浏览器发送的请求所匹配的控制器方法
interceptorList:处理控制器方法的所有拦截器集合
interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request,
mappedHandler.getHandler());
if (new ServletWebRequest(request,
response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 调用拦截器的preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
4、processDispatchResult()
private void processDispatchResult(HttpServletRequest request,
HttpServletResponse response,@Nullable HandlerExecutionChain
mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws
Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered",
exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler()
: null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 处理模型数据和渲染视图
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
// 调用拦截器的afterCompletion()
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
12.4、SpringMVC的执行流程
1、 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
b) 存在则执行下面的流程
3、 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及 Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。
4、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
5、如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
6、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定 的响应信息
b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
7、Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
8、此时将开始执行拦截器的postHandle(...)方法【逆向】。
9、根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行
HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
10、渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
11、 将渲染结果返回给客户端。