MVC 是什么
MVC 不是 Spring 发明的,而是一种设计模式,目的是“解耦”。
- M(Model,模型):数据 + 业务逻辑。比如
Teacher类,TeacherService。 - V(View,视图):展示数据的界面。比如 JSP、Thymeleaf 模板,或者是现代返回 JSON 的前端页面。
- C(Controller,控制器):接收用户请求,调用 Model,最后选择 View 来展示。
流程:用户点击一个链接 → Controller 拿到请求 → 调 Service 拿到数据(Model)→ 把数据交给 View 渲染 → 返回 HTML 给浏览器。
Spring MVC 就是把这个流程在 Java Web 环境里落地的一套框架。
核心组件与处理流程
Spring MVC 最核心的就是一个 前端控制器(Front Controller)——DispatcherServlet。
| 组件 | 作用 | 常见实现 |
|---|---|---|
| DispatcherServlet | 统一入口,调度一切 | Spring 提供,我们只需配置 |
| HandlerMapping | 根据请求 URL 找到对应的处理器 | RequestMappingHandlerMapping |
| HandlerAdapter | 执行找到的处理器(Controller 方法) | RequestMappingHandlerAdapter |
| HandlerInterceptor | 拦截器,在处理方法前后做增强 | 自定义 |
| ViewResolver | 根据视图名找到真正的视图文件 | InternalResourceViewResolver (JSP) |
| HandlerExceptionResolver | 处理异常 | ExceptionHandlerExceptionResolver |
一个请求的完整生命周期(回顾前面讲过的流程图):
- 请求到达
DispatcherServlet。 DispatcherServlet找HandlerMapping:谁处理这个 URL?HandlerMapping返回一个HandlerExecutionChain(包含 Controller 方法 + 一堆拦截器)。DispatcherServlet找HandlerAdapter:谁能执行这个 Controller 方法?HandlerAdapter执行具体方法(期间会做参数绑定、类型转换、校验等)。- 方法返回
ModelAndView(或@ResponseBody直接返回数据)。 - 若有视图名,
ViewResolver解析出真正的 JSP 等视图。 - 渲染视图,响应给浏览器。
Controller 与注解:从入门到精通
@Controller & @RestController
1@Controller // 声明这是一个控制器类,方法通常返回视图名 2public class TeacherController { ... } 3 4@RestController // = @Controller + @ResponseBody,所有方法默认返回 JSON 5public class TeacherRestController { ... } 6
@RequestMapping 及衍生注解
1@RequestMapping("/teacher") // 类级别映射 2public class TeacherController { 3 4 @RequestMapping(value = "/list", method = RequestMethod.GET) 5 public String list() { ... } // 等价于 @GetMapping("/list") 6} 7
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping 等都是 @RequestMapping 的快捷方式。
参数绑定注解(重点对比)
| 注解 | 从哪里拿数据 | 示例 |
|---|---|---|
| @RequestParam | URL 问号后的参数或表单数据 | ?name=Tom → @RequestParam("name") String name |
| @PathVariable | URI 路径中的占位符 | /teacher/{id} → @PathVariable("id") Long id |
| @RequestBody | 请求体中的 JSON/XML | POST 的 JSON {"name":"Tom"} 自动转成 Teacher 对象 |
| @ModelAttribute | ① 从 Model 中取;② 把参数绑定到对象 | 常用于表单提交自动封装对象, |
| @RequestHeader | 请求头 | @RequestHeader("User-Agent") String ua |
| @CookieValue | Cookie | @CookieValue("JSESSIONID") String sid |
把参数自动封装成对象
Spring 可以直接将表单字段或 JSON 映射成一个 Java Bean。
表单提交示例:
1<form action="/teacher/save" method="post"> 2 <input name="name"/> <!-- Teacher.name --> 3 <input name="age"/> <!-- Teacher.age --> 4 <input type="submit"/> 5</form> 6
1@PostMapping("/teacher/save") 2public String save(@ModelAttribute Teacher teacher) { 3 // teacher 对象已被自动填充 name 和 age 4 teacherService.save(teacher); 5 return "redirect:/teacher/list"; 6} 7
底层原理:DataBinder + BeanWrapper 负责属性拷贝。
自定义类型转换器:String → Date
很多时候,请求参数是 String,但你想转成 Date、LocalDate 或自定义类型。
Spring 内置的转换器和格式化器
- Converter<S, T>:源类型 → 目标类型,如
StringToDateConverter。 - Formatter:专为字符串和对象互转设计,支持国际化,适合
String <-> Date。
自定义一个 String → Date 的 Converter 示例
1@Component 2public class StringToDateConverter implements Converter<String, Date> { 3 private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 4 5 @Override 6 public Date convert(String source) { 7 try { 8 return format.parse(source); 9 } catch (ParseException e) { 10 throw new IllegalArgumentException("日期格式必须为 yyyy-MM-dd"); 11 } 12 } 13} 14
在 Spring Boot 中,只需让它被容器管理(@Component)就会自动注册到转换器链。
在传统 Spring MVC XML 中,需要配置 FormattingConversionServiceFactoryBean。
使用场景
1@GetMapping("/teacher/search") 2public String searchByDate(@RequestParam("date") Date date) { 3 // /teacher/search?date=2025-01-01 → date 直接转换好 4} 5
如果转换失败,会抛出 TypeMismatchException,我们可以用异常处理来统一返回友好信息。
拦截器(Interceptor)和过滤器(Filter)
两者都可以在请求前后做手脚,但层级不同。
| 对比维度 | Filter(过滤器) | Interceptor(拦截器) |
|---|---|---|
| 归属 | Servlet 规范,Java EE | Spring MVC 框架 |
| 作用范围 | 能拦截所有进入 Servlet 的请求(包括静态资源) | 只能拦截进到 Spring MVC 的请求(DispatcherServlet 处理) |
| 是否能用 Spring Bean | 不能直接注入(可以迂回) | 可以正常注入其他 Bean |
| 执行顺序 | 先经过 Filter,再进 DispatcherServlet | 在 DispatcherServlet 之后,Controller 方法之前 |
| 典型场景 | 字符编码、跨域、权限安全检查 | 登录检查、日志记录、性能监控、用户权限补充 |
定义拦截器示例
1@Component 2public class LoginInterceptor implements HandlerInterceptor { 3 @Override 4 public boolean preHandle(HttpServletRequest request, 5 HttpServletResponse response, 6 Object handler) { 7 // 检查 session 中是否有用户 8 if (request.getSession().getAttribute("user") == null) { 9 response.sendRedirect("/login"); 10 return false; // 不放行 11 } 12 return true; 13 } 14} 15
注册拦截器
1@Configuration 2public class WebConfig implements WebMvcConfigurer { 3 @Autowired 4 private LoginInterceptor loginInterceptor; 5 6 @Override 7 public void addInterceptors(InterceptorRegistry registry) { 8 registry.addInterceptor(loginInterceptor) 9 .addPathPatterns("/teacher/**") // 要拦截的 10 .excludePathPatterns("/teacher/login"); // 排除的 11 } 12} 13
异常处理:优雅地给前端报错
还记得我们之前聊过的“异常和错误”吗?常用的处理方式是:
局部异常处理
1@Controller 2public class TeacherController { 3 @ExceptionHandler(TeacherNotFoundException.class) 4 public String handleNotFound(TeacherNotFoundException ex, Model model) { 5 model.addAttribute("msg", ex.getMessage()); 6 return "error/404"; // 返回错误视图 7 } 8} 9
全局异常处理(推荐)
1@ControllerAdvice 2public class GlobalExceptionHandler { 3 4 @ExceptionHandler(MethodArgumentNotValidException.class) 5 @ResponseStatus(HttpStatus.BAD_REQUEST) 6 @ResponseBody 7 public Map<String, String> handleValidation(MethodArgumentNotValidException ex) { 8 // 返回校验失败信息,如 {"name":"不能为空"} 9 ... 10 } 11 12 @ExceptionHandler(Exception.class) 13 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 14 @ResponseBody 15 public Map<String, String> handleGeneral(Exception ex) { 16 return Map.of("error", "服务器内部错误"); 17 } 18} 19
《Spring MVC 的核心知识点梳理》 是转载文章,点击查看原文。