通过这一个帖子,我们会涉及到以下问题:

MVC(Model-View-Controller)是一种软件设计模式,将应用程序分为模型(处理数据和业务逻辑)、视图(用户界面)和控制器(处理用户请求和协调模型与视图)。Spring MVC 是基于 Spring 框架的 MVC 实现,简化了 Web 层开发,使代码更易维护和测试。
MVC 模式通过分离业务逻辑、数据和界面,让代码更清晰。早期 JavaWeb 开发中,Model 1 时代只用 JSP 页面,导致代码混乱;Model 2 时代引入了 Servlet 作为控制器,但仍有不足。现在,Spring MVC 成为主流,它轻量、高效,与 Spring 集成,帮助开发者快速构建 Web 应用,一般将项目分为 Controller、Service、Dao 等层,使开发更简洁。
那么 SpringMVC 的处理流程是怎么样的呢?这个范围太大了,我们可以将工作流程拆解为核心组件与工作流程来分析。其中,SpringMVC 的核心组件有:
-
DispatcherServlet:核心的中央处理器,负责接收请求、分发,并给予客户端响应
-
HandlerMapping:处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装
-
HandlerAdapter:处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler
-
Handler:请求处理器,处理实际请求的处理器
-
ViewResolver:视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
其中 HandlerMapping 与 HandlerAdapter 可以留意一下。
| HandlerMapping | 负责将请求映射到处理器(Controller) | 根据请求的 URL、请求参数 等信息,找到处理请求的 Controller | BeanNameUrlHandlerMapping, RequestMappingHandlerMapping |
| HandlerAdapter | 负责调用处理器(Controller)来处理请求 | 由于 Controller 的实现方式多样,它根据处理器的类型,选择合适的方法来调用它 | 用于适配 Controller 接口、HttpRequestHandler 接口等的实现类 |
用大白话来说:
-
HandlerMapping 就像是公司的前台引导员。你(请求)说要找谁,他根据你的需求(就是 URL ),查表找到具体处理你事情的负责人(Controller)是谁。
-
HandlerAdapter 就像是负责人的专属助理。每个负责人的工作方式可能不同(比如有的说中文,有的说英文),助理负责适配并调用负责人,让他开始干活。
SpringMVC 的核心组件我们了解了,就可以继续来学习工作原理,可以直接用一张图表示(很重要):

-
客户端(浏览器)发送请求, DispatcherServlet(核心的中央处理器)拦截请求
-
DispatcherServlet(核心的中央处理器)根据请求信息调用 HandlerMapping(处理映射器)。根据之前的核心组件分析,我们可以知道 HandlerMapping 负责将请求映射到处理器(Controller);所以 HandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler(请求处理器) 一起封装。所以在第四步的时候,能看到 Handler 与 controller 是在同一个小方框里的
-
DispatcherServlet 调用 HandlerAdapter 适配器执行 Handler
-
Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给 DispatcherServlet ,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View
-
ViewResolver(视图解析器) 会根据逻辑 View 查找实际的 View
-
DispaterServlet 把返回的 Model 传给 View(视图渲染)
-
把 View 返回给请求者(浏览器)
上述流程是传统开发模式(JSP,Thymeleaf 等)的工作原理。但是在现在前后端分离的主流下,后端只通过API提供数据,前端(Vue/React等)负责渲染页面。
-
核心变化:后端不再关心视图(View),只返回纯数据(通常是JSON)。
-
流程简化:上面的第1-4步保持不变,但从第5步开始不同:
-
Controller处理完后,不再返回 ModelAndView ,而是直接返回数据对象。
-
ViewResolver不再工作,因为不需要解析视图。
-
Spring会自动将返回的数据转换为JSON格式。
-
-
如何实现:非常简单,在Controller类上使用 @RestController 注解,或者使用@Controller注解的同时,在每个方法上加上 @ResponseBody 注解即可。
最后再来介绍一下在 SpringMVC 中统一异常处理是怎么做的:使用 @ControllerAdvice 和 @ExceptionHandler 这两个注解来全局统一地处理异常。
实现起来非常简单,只需要两步:
-
创建一个全局处理类:在一个类上加上 @ControllerAdvice 注解。这个注解就像一个“监听器”,告诉Spring:“这个类是专门用来处理所有Controller的异常的”。
-
编写处理方法:在这个类里面,你可以写很多方法,在每个方法上使用 @ExceptionHandler 注解,并指定你要处理的异常类型(比如 @ExceptionHandler(SQLException.class))。这个方法就是用来处理对应异常的。
@ControllerAdvice
public class GlobalExceptionHandler {
// 当Controller抛出 NullPointerException 时,这个方法会被调用
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<String> handleNullPointer(NullPointerException ex) {
// 在这里,你可以记录日志,然后返回一个友好的JSON错误信息给前端
return ResponseEntity.badRequest().body("服务器遇到了空指针错误,请稍后再试。");
}
// 当Controller抛出 IOException 时,这个方法会被调用
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIO(IOException ex) {
return ResponseEntity.status(500).body("文件读写失败。");
}
}
当一个异常被抛出时,Spring是怎么知道该调用哪个方法的呢?这里有一个优先级规则:
-
找最匹配的: Spring会从所有被 @ExceptionHandler 注解的方法中,找出能处理该异常类型(或其父类)的方法。
-
如果有多个方法都能处理(比如一个方法处理Exception,另一个处理IOException),Spring会选择那个处理最具体、最精确异常类型的方法。
这就像你家里出了问题,如果是“电器坏了”,可以找你爸(处理Exception,所有异常的父类);但如果是“电视机坏了”,就会优先找你哥(处理IOException),因为他更懂电视机,处理得更专业。
在前后端分离的项目中,我们通常在异常处理方法中直接返回一个JSON对象(就像上面的例子一样),里面包含错误码和友好的错误信息,而不是返回一个错误页面。前端拿到这个JSON后,再以弹窗等形式展示给用户。
2065

被折叠的 条评论
为什么被折叠?



