层层递进的关系来分析 SpringBoot 的Web模块源码
1. SpringBoot Web源码分析(一) 静态资源源码分析
2. SpringBoot Web源码分析(二) 请求处理源码分析
3. SpringBoot Web源码分析(三) 响应处理源码分析
4. SpringBoot Web源码分析(四) 视图解析器源码分析
5. SpringBoot Web源码分析(五),拦截器和文件上传
6. SpringBoot Web源码分析(六) 异常处理和Servlet组件
7. SpringBoot Web源码分析(七) 嵌入式服务器和定制化原理
1.5 拦截器
1.5.1 拦截器基本用法
1. 声明一个自己的拦截器,实现 HandlerInterceptor 接口。
2. 将这个拦截器注册到容器中
如果我们想在 web 配置中加入我们自己的配置时,就需要创建一个配置类,实现 WebMvcConfigurer 接口,在这里面添加上我们自己额外的配置。
WebMvcConfigurer 接口
根据方法就能看到里面有一个 addInterceptors 方法,添加我们自定义的拦截器
最后将拦截器添加到容器中,这样这个拦截器就能生效了
1.5.1 拦截器源码分析
1. 根据当前请求,找到 HandlerExecutionChain
doDispatch() 中的getHandler 方法,得到处理器(可以处理请求的handler以及handler的所有拦截器)
2. 顺序执行所有拦截器的preHandler 方法
如果返回为true 则执行下一个拦截器。
如果返回为false则执行triggerAfterCompletion方法,倒序执行(i-- 的for循环方式)
3. 如果任何一个拦截器都返回 false,直接不执行目标方法
4. 所有拦截器都返回 true,执行目标方法
5. 执行PostHandler方法
我们点进去发现也是倒序执行(使用 i-- 的方式)
6. 异常直接触发afterCompletion
7. 页面渲染完成触发 afterCompletion
(第一行是页面进行渲染的方法,后面无论是否发生异常都会运行afterCompletion方法)
这里也从源码的角度证明了拦截器中 afterCompletion 方法是一定会执行的
拦截器总结图
1.6 文件上传
1.6.1 文件上传示例
1. 页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data">
<label>单文件</label>
<input type="file" name="signalFile">
<label>多文件</label>
<input type="file" multiple name="multipleFile">
<input type="submit" value="提交">
</form>
</body>
</html>
2. Controller 处理并保存到磁盘
@PostMapping("/upload")
public String upload(@RequestPart("signalFile") MultipartFile signalFile,
@RequestPart("multipleFile") MultipartFile[] multipleFile) throws IOException {
signalFile.transferTo(new File("D:\\imgs\\" + signalFile.getOriginalFilename()));
for (MultipartFile file : multipleFile) {
file.transferTo(new File("D:\\imgs\\" + file.getOriginalFilename()));
}
return "file";
}
1.6.2 文件上传原理
文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
-
自动配置好了 StandardServletMultipartResolver 【标准的Servlet文件上传解析器】
-
原理步骤
- 请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
判断是否是一个文件上传请求,如果是就返回这个请的封装类 两个判断过程 判断multipartResolver是否为空
然后判断表单头部是否是包含 multipart/
如果确定是一个文件上传请求,就会调用对其进行封装的方法封装过程
调用StandardServletMultipartResolver实现的resolveMultipart方法
-
参数解析器来解析请求中的文件内容封装成MultipartFile(详情请看参数解析器的源码分析)
-
将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>