SpringMVC核心知识点实操笔记(参数绑定+返回值+文件上传+拦截器+类型转换)
本篇基于SpringMVC入门实操代码整理,覆盖日常开发最核心的五大模块:请求参数绑定、Controller返回值处理、文件上传三种实现、拦截器执行流程、自定义类型转换器。所有知识点均配套对应代码示例,适合入门学习与面试复习。
一、请求参数绑定全解
SpringMVC的核心能力之一就是自动参数绑定:框架会自动将HTTP请求中的参数,按照规则映射到Controller方法的入参上,支持多种数据类型与注解扩展。
1.1 基本数据类型与字符串
直接在方法形参上声明对应类型,请求参数名与形参名保持一致即可自动注入。
java
@RequestMapping("/save1")
public String save(String username, Integer age, Date birthday) {
System.out.println("姓名:" + username);
System.out.println("年龄:" + age);
System.out.println("生日:" + birthday);
return "suc";
}
注意:基本数据类型(如
int)要求参数必须传值,否则空指针报500;包装类型(如Integer)允许为null,更推荐使用。
1.2 JavaBean实体类型
请求参数名与实体类的属性名一一对应时,框架会自动反射封装成完整的Java对象。
java
@RequestMapping("/save2")
public String save2(User user){
System.out.println("user对象:"+user);
return "suc";
}
支持级联属性自动封装,例如user.address.city这种嵌套对象属性也能自动赋值。
1.3 集合类型(List/Map)
当实体类中包含List、Map集合时,通过特定的参数命名规则即可完成绑定:
- List集合:
list[0].name、list[1].name - Map集合:
map['key1'].name、map['key2'].name
java
@RequestMapping("/save4")
public String save4(User user) {
System.out.println("user对象:" + user);
return "suc";
}
1.4 原生Servlet API注入
直接在方法形参上声明HttpServletRequest、HttpServletResponse、HttpSession,SpringMVC会自动注入原生对象。
java
@RequestMapping("/save6")
public String save6(HttpServletRequest request, HttpServletResponse response) {
System.out.println("request: " + request);
HttpSession session = request.getSession();
System.out.println("session: " + session);
return "suc";
}
1.5 核心参数注解
@RequestParam
解决形参名与请求参数名不一致的问题,同时可设置是否必填、默认值。
java
@RequestMapping("/save7")
public String save7(@RequestParam(value = "username", required = false, defaultValue = "abc") String name){
System.out.println("姓名:"+name);
return "suc";
}
value:请求参数的名称required:是否必须传该参数,默认truedefaultValue:参数不传时的默认值
@RequestBody
用于接收请求体中的JSON数据,自动反序列化为Java对象,仅支持POST请求。
java
@RequestMapping("/save8")
public String save8(@RequestBody User user) {
System.out.println("请求体内容:" + user);
return "suc";
}
前提:需要导入jackson等JSON转换依赖,SpringMVC自动配置消息转换器完成序列化/反序列化。
@PathVariable
RESTful风格开发必备,从URL路径中提取占位符变量值。
java
@RequestMapping(path = "/emp/{id}", method = RequestMethod.GET)
public String findById(@PathVariable(value = "id") Integer id){
System.out.println("通过id查询员工..."+id);
return "suc";
}
@RequestHeader / @CookieValue
@RequestHeader:获取指定名称的请求头的值@CookieValue:获取指定名称的Cookie值
java
// 获取Accept请求头
public String save9(@RequestHeader(value = "Accept") String header)
// 获取JSESSIONID的Cookie值
public String save10(@CookieValue(value = "JSESSIONID") String cookie)
1.6 @RequestMapping进阶属性
params:限定请求必须携带指定参数,否则无法访问接口method:限定允许的请求方式(GET/POST/PUT/DELETE等)
java
// 必须同时携带username和age参数才能访问
@RequestMapping(path = "/hello1", params = {"username","age"})
// 只允许POST请求访问
@RequestMapping(path = "/hello2", method = RequestMethod.POST)
1.7 RESTful风格接口示例
通过请求方式区分增删改查,同一URL对应不同业务操作:
java
// 新增员工:POST请求
@RequestMapping(path = "/emp", method = RequestMethod.POST)
// 查询全部员工:GET请求
@RequestMapping(path = "/emp", method = RequestMethod.GET)
// 根据id查询:GET请求 + 路径变量
@RequestMapping(path = "/emp/{id}", method = RequestMethod.GET)
二、Controller返回值与页面跳转
2.1 返回String(最常用)
返回字符串作为逻辑视图名,由视图解析器拼接前缀和后缀,找到真实JSP页面,默认采用请求转发。
java
@RequestMapping("/save1")
public String save1(){
System.out.println("执行了...");
return "suc"; // 拼接后:/WEB-INF/pages/suc.jsp
}
2.2 返回void
默认会根据请求路径自动拼接视图地址,找不到页面就报404。正确的打开方式有三种:
- 直接通过response输出响应内容,不走视图解析
- 手动调用request进行请求转发
- 手动调用response进行重定向
java
@RequestMapping("/save2")
public void save2(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("执行了...");
// 直接输出数据,SpringMVC不会再走视图解析流程
response.getWriter().print("hello");
}
2.3 返回ModelAndView
SpringMVC原生返回方式,同时封装模型数据 和视图信息。
java
@RequestMapping("/save3")
public ModelAndView save3(){
ModelAndView mv = new ModelAndView();
// 存入数据,相当于存到request域中
mv.addObject("msg","用户名或者密码已经存在");
// 设置逻辑视图名称
mv.setViewName("suc");
return mv;
}
2.4 转发与重定向的核心区别(面试高频)
基础语法
java
// 请求转发
return "forward:/WEB-INF/pages/suc.jsp";
// 重定向
return "redirect:/user/save1";
8大核心区别对比
| 对比维度 | 请求转发 forward | 重定向 redirect |
|---|---|---|
| 地址栏URL | 保持不变,显示原接口地址 | 发生变化,更新为跳转后的新地址 |
| request域数据 | 共享,同一次请求,数据可传递 | 不共享,两次独立请求,数据丢失 |
| 请求次数 | 1次,服务器内部完成跳转 | 2次,浏览器收到302后重新发起 |
| 跳转范围 | 仅能访问当前项目内部资源 | 可跳转外部网站、第三方域名 |
| 表单重复提交 | 刷新页面会重复提交表单 | 刷新页面只会重新查询,不会重复提交 |
| 性能 | 更高,仅一次网络往返 | 稍低,两次网络往返 |
| HTTP状态码 | 返回200正常状态 | 返回302临时重定向状态 |
| 适用场景 | 查询页面跳转、需传递大量数据 | 增删改操作后、跳转外部地址 |
开发规范:所有新增、修改、删除操作完成后,必须使用重定向跳转到查询页,避免用户刷新页面造成表单重复提交。
2.5 @ResponseBody响应JSON
加在方法上后,返回值会直接作为响应体输出,不再走视图解析器。返回Java对象时,框架自动序列化为JSON字符串。
java
@PostMapping("/save6")
@ResponseBody
public User save6(@RequestBody User user){
System.out.println(user);
user.setUsername("hello");
user.setAge(100);
// 自动转为JSON字符串响应给前端
return user;
}
三、文件上传三种实现方式
3.1 文件上传前置条件
- 表单提交方式必须为
method="post" - 表单编码类型必须设置为
enctype="multipart/form-data"(默认编码无法传输二进制文件) - 后端需要对应的文件解析组件
3.2 方式一:原生commons-fileupload
Servlet 2.x时代的通用方案,依赖Apache的commons-fileupload和commons-io,手动解析请求中的表单项。
java
// 核心执行流程
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(10240); // 10KB以下存内存
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(5 * 1024 * 1024); // 单文件最大5MB
// 解析请求得到所有表单项
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// 普通表单字段,获取name和value
} else {
// 文件字段,写入磁盘
item.write(savedFile);
item.delete(); // 清理临时文件
}
}
- 优点:底层可控,兼容所有Servlet版本
- 缺点:代码冗余,需手动处理文件名、临时文件、大小限制
3.3 方式二:Servlet 3.0 Part API
Servlet 3.0(Tomcat 7+)原生支持,无需第三方依赖,通过@MultipartConfig注解配置参数。
java
@MultipartConfig(
fileSizeThreshold = 10240,
maxFileSize = 5 * 1024 * 1024, // 单文件最大5MB
maxRequestSize = 10 * 1024 * 1024 // 总请求最大10MB
)
public class PartUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
Part filePart = request.getPart("uploadFile");
String fileName = filePart.getSubmittedFileName();
// 保存文件,参数必须是绝对路径
filePart.write(absolutePath);
}
}
- 优点:零第三方依赖,API简洁,容器原生支持
- 缺点:仅支持Servlet 3.0及以上环境
3.4 方式三:SpringMVC MultipartFile(企业开发首选)
SpringMVC封装的标准方案,也是实际开发中最常用的方式。需先配置文件上传解析器,Controller直接声明形参即可接收。
第一步:配置文件上传解析器
XML配置方式(id必须固定为multipartResolver):
xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxFileSize" value="5242880"/> <!-- 单文件最大5MB -->
<property name="maxUploadSize" value="10485760"/><!-- 总请求最大10MB -->
</bean>
第二步:单文件上传
java
@RequestMapping("/fileupload")
public String upload(MultipartFile upload, HttpServletRequest request) throws IOException {
// 获取项目内uploads目录的真实路径
String realPath = request.getSession().getServletContext().getRealPath("/uploads");
File dir = new File(realPath);
if(!dir.exists()) dir.mkdirs();
// 获取原始文件名
String filename = upload.getOriginalFilename();
// UUID重命名,防止文件名重复覆盖
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
filename = uuid + "_" + filename;
// 核心方法:将上传文件保存到目标位置
upload.transferTo(new File(dir, filename));
return "suc";
}
第三步:批量文件上传
用数组接收多个同名文件,循环逐个保存:
java
@RequestMapping("/batchFileupload")
public String batchUpload(MultipartFile[] uploads, HttpServletRequest request) throws IOException {
String realPath = request.getSession().getServletContext().getRealPath("/uploads");
File dir = new File(realPath);
if (!dir.exists()) dir.mkdirs();
for (MultipartFile upload : uploads) {
if (upload.isEmpty()) continue; // 跳过空文件
String originalName = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
String finalName = uuid + "_" + originalName;
upload.transferTo(new File(dir, finalName));
}
return "suc";
}
- 优点:代码极简,与SpringMVC无缝集成,支持普通参数与文件混传
- 企业开发中90%以上的文件上传场景都采用此方案
3.5 拓展:大文件分片上传原理
针对GB级大文件,解决HTTP超时、内存溢出、网络中断无法续传的问题。
核心五步流程
- 前端切片 :通过
File.slice()将文件切成1~10MB的分片,携带fileId、分片序号、总分片数 - 逐片上传:可开启3~6个并发提升传输速度
- 后端存片 :每个分片存为临时文件
{fileId}_{chunkIndex}.part - 合并文件:全部上传完成后,按分片序号顺序合并成完整文件
- 完整性校验:通过MD5校验文件是否完整
进阶能力
- 秒传(去重):上传前先校验文件MD5,服务端已存在相同文件则直接返回地址,跳过整个上传流程
- 断点续传:上传前查询已上传的分片列表,跳过已传输完成的分片,从中断处继续
四、拦截器Interceptor
4.1 拦截器定义
实现HandlerInterceptor接口,用于在Controller方法执行前后进行拦截,常用于权限校验、日志记录、统一编码处理、性能监控等场景。
java
public class MyInterceptor implements HandlerInterceptor{
/**
* Controller方法执行前执行
* return true 放行,执行后续逻辑;return false 拦截请求
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("preHandle方法执行了...");
return true;
}
/**
* Controller方法执行后,视图渲染前执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("postHandle方法执行了...");
}
/**
* 视图渲染完成后最后执行,用于资源清理、异常记录
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("afterCompletion方法执行了...");
}
}
4.2 执行顺序
- 单个拦截器:
preHandle → Controller方法 → postHandle → 页面渲染 → afterCompletion - 多个拦截器:preHandle按配置顺序正序 执行,postHandle和afterCompletion按配置顺序倒序执行
4.3 拦截器 vs 过滤器
- 过滤器是Servlet规范定义的,能拦截所有Web请求;拦截器是SpringMVC组件,只拦截Controller方法
- 过滤器执行时机早于拦截器
- 拦截器可以注入Spring容器中的Bean,过滤器不行
五、自定义类型转换器
5.1 为什么需要自定义转换器
SpringMVC自带的类型转换无法覆盖所有场景,比如前端传入yyyy-MM-dd格式的日期字符串,默认无法直接转为Date对象,此时就需要自定义转换规则。
5.2 实现方式
实现Converter<S, T>接口,S是源类型,T是目标类型。
java
public class StringToDate implements Converter<String, Date>{
@Override
public Date convert(String s) {
if (s == null) {
throw new RuntimeException("请输入日期内容");
}
// 支持多种日期格式兼容
String[] patterns = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyy-MM-d", "yyyy/MM/d"};
for (String pattern : patterns) {
try {
return new SimpleDateFormat(pattern).parse(s);
} catch (ParseException ignored) {
}
}
throw new RuntimeException("日期格式不合法,支持: yyyy-MM-dd 或 yyyy/MM/dd");
}
}
5.3 注册生效
编写完成后,需要将自定义转换器注册到SpringMVC的类型转换服务中,配置完成后才会生效。
六、全局统一异常处理
6.1 为什么需要全局异常处理
在日常开发中,如果在每个 Controller 方法内部都通过 try-catch 捕获异常,会造成大量重复代码,业务逻辑与异常处理高度耦合,也不利于统一错误提示和日志排查。
Spring MVC 提供了全局异常处理机制,可以统一拦截 Controller 层抛出的所有异常,集中进行日志记录、友好提示封装和页面跳转,实现业务代码与异常处理逻辑的解耦。
6.2 自定义业务异常类
系统中的异常通常分为两类:
- 业务异常:预期内的异常,由用户违规操作、业务校验不通过等引起,需要携带明确的提示信息返回给用户
- 系统异常:预期外的异常,如空指针、数据库连接失败、网络超时等,属于系统底层错误,不应将详细信息暴露给用户
我们通过自定义异常类来封装业务异常,便于全局处理器识别和处理:
java
package com.qcby.exception;
/**
* 自定义业务异常类
*/
public class SysException extends Exception{
// 异常提示消息
private String message;
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}
6.3 全局异常处理器实现
Spring MVC 提供了 HandlerExceptionResolver 接口,实现该接口并交由 Spring 容器管理后,即可成为全局异常处理器,自动拦截所有 Controller 抛出的异常。
java
package com.qcby.exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理器
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 程序抛出异常后,自动执行该方法处理异常
* @param request 当前请求对象
* @param response 当前响应对象
* @param handler 异常发生的处理器(Controller方法)
* @param e 抛出的异常对象
* @return 异常处理后的视图与模型数据
*/
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception e) {
// 1. 打印异常堆栈,便于后台排查问题
e.printStackTrace();
SysException exception;
// 2. 区分异常类型,封装友好提示
if(e instanceof SysException){
// 业务异常:直接取出预设的提示信息
exception = (SysException) e;
}else{
// 系统异常:封装通用友好提示,隐藏底层错误细节
exception = new SysException("系统正在维护,请联系管理员");
}
// 3. 封装模型数据与视图,跳转到统一错误页面
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg", exception.getMessage());
mv.setViewName("error");
return mv;
}
}
注意:最终存入模型的应当是封装后的
exception.getMessage(),而非原始异常e.getMessage(),避免系统异常的底层错误信息直接暴露给前端用户,造成安全风险。
6.4 配置生效
自定义的异常处理器必须交由 Spring IoC 容器管理才能生效,有两种常用配置方式:
- XML 配置方式(传统SSM项目)
xml
<!-- 注册全局异常处理器,id无强制要求 -->
<bean class="com.qcby.exception.SysExceptionResolver"/>
- 注解配置方式 (全注解开发项目)
在SysExceptionResolver类上添加@Component注解,配合组件扫描自动注入容器。
6.5 异常处理执行流程
- Controller 方法执行业务逻辑,出现异常时向上抛出
- DispatcherServlet 捕获到异常,查找并委托给全局异常处理器
- 异常处理器执行
resolveException方法,完成异常日志记录、信息封装 - 返回
ModelAndView对象,Spring MVC 按正常视图解析流程渲染统一错误页面
6.6 Spring MVC 四种异常处理方案对比
除了实现 HandlerExceptionResolver 接口,Spring MVC 还提供了另外三种常用的异常处理方式,适用于不同场景:
| 实现方式 | 作用范围 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
@ExceptionHandler 局部异常 |
当前Controller类内部 | 无需额外配置,灵活度高 | 只能作用于单个Controller,代码冗余 | 单个Controller专属异常处理 |
@ControllerAdvice + @ExceptionHandler |
全局 | 注解驱动、支持按异常类型精准匹配、代码简洁 | Spring 3.2 以上版本支持 | 主流项目首选,前后端分离、异步请求场景 |
实现 HandlerExceptionResolver 接口 |
全局 | 底层可控,兼容所有版本 | 异常类型判断需手动实现,代码稍繁琐 | 传统SSM项目、需要深度定制异常处理逻辑 |
SimpleMappingExceptionResolver |
全局 | 纯配置、零代码 | 无法携带自定义业务数据,灵活性差 | 简单项目、仅需跳转错误页面的场景 |
小结(更新版)
以上就是 Spring MVC 从入门到进阶的核心实操知识点,覆盖请求参数绑定、Controller 返回值与页面跳转、三种文件上传实现方案、拦截器执行机制、自定义类型转换器、全局异常处理六大核心模块,基本覆盖了传统 SSM 项目日常开发的绝大多数场景。理解底层执行原理、结合代码多动手实操,就能快速掌握 Spring MVC 的核心用法与常见面试考点。