参考视频:SpringMVC教程,SpringMVC从零到精通,老杜SpringMVC,动力节点SpringMVC
第8章 文件上传与下载
1. 文件上传
使用SpringMVC6版本,不需要添加以下依赖:
1 2 3 4 5
| <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.5</version> </dependency>
|
1、前端页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>文件上传</title> </head> <body>
<form th:action="@{/file/up}" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="fileName"/><br> <input type="submit" value="上传"> </form>
</body> </html>
|
PS:form表单采用post请求,enctype是multipart/form-data
,并且上传组件是:type="file"
2、web.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <servlet> <servlet-name>dispatcherServlet</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> <multipart-config> <max-file-size>102400</max-file-size> <max-request-size>102400</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
|
PS:在DispatcherServlet配置时,添加 multipart-config 配置信息。(这是Spring6,如果是Spring5,则不是这样配置,而是在springmvc.xml文件中配置:CommonsMultipartResolver)SpringMVC6中把这个类已经删除了。废弃了。
3、Controller中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Controller public class FileController {
@PostMapping(value = "/file/up") public String fileUp(@RequestParam("fileName") MultipartFile multipartFile, HttpServletRequest request) throws IOException { String name = multipartFile.getName(); System.out.println(name); String originalFilename = multipartFile.getOriginalFilename(); System.out.println(originalFilename); InputStream in = multipartFile.getInputStream(); File file = new File(request.getServletContext().getRealPath("/upload")); if (!file.exists()) { file.mkdirs(); } BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf(".")))); byte[] bytes = new byte[1024 * 100]; int readCount = 0; while ((readCount = in.read(bytes)) != -1) { out.write(bytes, 0, readCount); } out.flush(); in.close(); out.close();
return "ok"; }
}
|
4、最终测试结果:


建议:上传文件时,文件起名采用UUID。以防文件覆盖。
2. 文件下载
1、index.html
1 2
| <a th:href="@{/download}">文件下载</a>
|
2、文件下载核心程序,使用ResponseEntity
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @GetMapping("/download") public ResponseEntity<byte[]> downloadFile(HttpServletResponse response, HttpServletRequest request) throws IOException { File file = new File(request.getServletContext().getRealPath("/upload") + "/muyoukule.jpg"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDispositionFormData("attachment", file.getName());
ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK); return entity; }
|
效果:

第9章 异常处理器
1. 什么是异常处理器
Spring MVC在处理器方法
执行过程中出现了异常,可以采用异常处理器
进行应对。
一句话概括异常处理器作用:处理器方法执行过程中出现了异常,跳转到对应的视图,在视图上展示友好信息。
SpringMVC为异常处理提供了一个接口:HandlerExceptionResolver
1 2 3 4 5 6 7 8 9 10
| package org.springframework.web.servlet;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.lang.Nullable;
public interface HandlerExceptionResolver { @Nullable ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); }
|
核心方法是:resolveException
。
该方法用来编写具体的异常处理方案。返回值ModelAndView
,表示异常处理完之后跳转到哪个视图。
HandlerExceptionResolver 接口有两个常用的默认实现:
DefaultHandlerExceptionResolver
SimpleMappingExceptionResolver
2. 默认的异常处理器
DefaultHandlerExceptionResolver
是默认的异常处理器。
核心方法:

当请求方式和处理方式不同时,DefaultHandlerExceptionResolver
的默认处理态度是:

3. 自定义的异常处理器
自定义异常处理器需要使用:SimpleMappingExceptionResolver
自定义异常处理机制有两种语法:
3.1 配置文件方式
1 2 3 4 5 6 7 8 9 10
| <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.Exception">tip</prop> </props> </property> <property name="exceptionAttribute" value="e"/> </bean>
|
在视图页面上展示异常信息:
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>出错了</title> </head> <body> <h1>出错了,请联系管理员!</h1> <div th:text="${e}"></div> </body> </html>
|

3.2 注解方式
1 2 3 4 5 6 7 8 9
| @ControllerAdvice public class ExceptionController {
@ExceptionHandler public String tip(Exception e, Model model){ model.addAttribute("e", e); return "tip"; } }
|
第10章 拦截器
1. 拦截器概述
拦截器(Interceptor)类似于过滤器(Filter)
Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截,可以对请求和响应进行一些特定的处理。
拦截器可以用于很多场景下:
- 登录验证:对于需要登录才能访问的网址,使用拦截器可以判断用户是否已登录,如果未登录则跳转到登录页面。
- 权限校验:根据用户权限对部分网址进行访问控制,拒绝未经授权的用户访问。
- 请求日志:记录请求信息,例如请求地址、请求参数、请求时间等,用于排查问题和性能优化。
- 更改响应:可以对响应的内容进行修改,例如添加头信息、调整响应内容格式等。
拦截器和过滤器的区别在于它们的作用层面不同。
- 过滤器更注重在请求和响应的流程中进行处理,可以修改请求和响应的内容,例如设置编码和字符集、请求头、状态码等。
- 拦截器则更加侧重于对控制器进行前置或后置处理,在请求到达控制器之前或之后进行特定的操作,例如打印日志、权限验证等。
Filter、Servlet、Interceptor、Controller的执行顺序:

2. 拦截器的创建与基本配置
2.1 定义拦截器
实现 org.springframework.web.servlet.HandlerInterceptor
接口,共有三个方法可以进行选择性的实现:
- preHandle:处理器方法调用之前执行(只有该方法有返回值,返回值是布尔类型,true放行,false拦截。)
- postHandle:处理器方法调用之后执行
- afterCompletion:渲染完成后执行
2.2 拦截器基本配置
在springmvc.xml文件中进行如下配置:
第一种方式:
1 2 3
| <mvc:interceptors> <bean class="com.muyoukule.interceptors.Interceptor1"/> </mvc:interceptors>
|
第二种方式:
1 2 3
| <mvc:interceptors> <ref bean="interceptor1"/> </mvc:interceptors>
|
第二种方式的前提:


PS:对于这种基本配置来说,拦截器是拦截所有请求的。
2.3 拦截器部分源码分析
2.3.1 方法执行顺序的源码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class DispatcherServlet extends FrameworkServlet { protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); mappedHandler.applyPostHandle(processedRequest, response, mv); processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { render(mv, request, response); mappedHandler.triggerAfterCompletion(request, response, null); } }
|
2.3.2 拦截与放行的源码分析
1 2 3 4 5 6 7 8 9
| public class DispatcherServlet extends FrameworkServlet { protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class HandlerExecutionChain { boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0; i < this.interceptorList.size(); i++) { HandlerInterceptor interceptor = this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } return true; } }
|
3. 拦截器的高级配置
采用以上基本配置方式,拦截器是拦截所有请求路径的。如果要针对某些路径进行拦截,某些路径不拦截,可以采用高级配置:
1 2 3 4 5 6 7 8 9 10
| <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/test"/> <ref bean="interceptor1"/> </mvc:interceptor> </mvc:interceptors>
|
以上的配置表示,除 /test
请求路径之外,剩下的路径全部拦截。
4. 拦截器的执行顺序
4.1 执行顺序
4.1.1 如果所有拦截器preHandle都返回true
按照springmvc.xml文件中配置的顺序,自上而下调用 preHandle:
1 2 3 4
| <mvc:interceptors> <ref bean="interceptor1"/> <ref bean="interceptor2"/> </mvc:interceptors>
|
执行顺序:

4.1.2 如果其中一个拦截器preHandle返回false
1 2 3 4
| <mvc:interceptors> <ref bean="interceptor1"/> <ref bean="interceptor2"/> </mvc:interceptors>
|
如果interceptor2
的preHandle
返回false
,执行顺序:

规则:只要有一个拦截器preHandle
返回false
,任何postHandle
都不执行。但返回false
的拦截器的前面的拦截器按照逆序执行afterCompletion
。
4.2 源码分析
DispatcherServlet
和 HandlerExecutionChain
的部分源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class DispatcherServlet extends FrameworkServlet { protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); mappedHandler.applyPostHandle(processedRequest, response, mv); processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { render(mv, request, response); mappedHandler.triggerAfterCompletion(request, response, null); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class HandlerExecutionChain { boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0; i < this.interceptorList.size(); i++) { HandlerInterceptor interceptor = this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } return true; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { for (int i = this.interceptorList.size() - 1; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }
|