努力成为面试高手04:Spring MVC

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

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、请求参数 等信息,找到处理请求的 ControllerBeanNameUrlHandlerMapping, RequestMappingHandlerMapping
HandlerAdapter负责调用处理器(Controller)来处理请求由于 Controller 的实现方式多样,它根据处理器的类型,选择合适的方法来调用它用于适配 Controller 接口、HttpRequestHandler 接口等的实现类

用大白话来说:

  • HandlerMapping 就像是公司的前台引导员。你(请求)说要找谁,他根据你的需求(就是 URL ),查表找到具体处理你事情的负责人(Controller)是谁。

  • HandlerAdapter 就像是负责人的专属助理。每个负责人的工作方式可能不同(比如有的说中文,有的说英文),助理负责适配并调用负责人,让他开始干活。

SpringMVC 的核心组件我们了解了,就可以继续来学习工作原理,可以直接用一张图表示(很重要):

  1. 客户端(浏览器)发送请求, DispatcherServlet(核心的中央处理器)拦截请求

  2. DispatcherServlet(核心的中央处理器)根据请求信息调用 HandlerMapping(处理映射器)。根据之前的核心组件分析,我们可以知道 HandlerMapping 负责将请求映射到处理器(Controller);所以 HandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler(请求处理器) 一起封装。所以在第四步的时候,能看到 Handler 与 controller 是在同一个小方框里的

  3. DispatcherServlet 调用 HandlerAdapter 适配器执行 Handler

  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给 DispatcherServlet ,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View

  5. ViewResolver(视图解析器) 会根据逻辑 View 查找实际的 View

  6. DispaterServlet 把返回的 Model 传给 View(视图渲染)

  7. 把 View 返回给请求者(浏览器)

上述流程是传统开发模式(JSP,Thymeleaf 等)的工作原理。但是在现在前后端分离的主流下,后端只通过API提供数据,前端(Vue/React等)负责渲染页面。

  • 核心变化:后端不再关心视图(View),只返回纯数据(通常是JSON)。

  • 流程简化:上面的第1-4步保持不变,但从第5步开始不同:

    • Controller处理完后,不再返回 ModelAndView ,而是直接返回数据对象。

    • ViewResolver不再工作,因为不需要解析视图。

    • Spring会自动将返回的数据转换为JSON格式。

  • 如何实现:非常简单,在Controller类上使用 @RestController 注解,或者使用@Controller注解的同时,在每个方法上加上 @ResponseBody 注解即可。

最后再来介绍一下在 SpringMVC 中统一异常处理是怎么做的:使用 @ControllerAdvice 和 @ExceptionHandler 这两个注解来全局统一地处理异常。

实现起来非常简单,只需要两步:

  1. 创建一个全局处理类:在一个类上加上 @ControllerAdvice 注解。这个注解就像一个“监听器”,告诉Spring:“这个类是专门用来处理所有Controller的异常的”。

  2. 编写处理方法:在这个类里面,你可以写很多方法,在每个方法上使用 @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后,再以弹窗等形式展示给用户。

参考

Spring常见面试题总结 | JavaGuide

Spring面试题 | 小林coding

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值