拼多多1面:聊聊Spring MVC的工作原理!

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖

嗨,你好啊,我是猿java

都说拼多多是程序员的终点站,这篇文章,我们来分析一道它的 1面题目:聊聊Spring MVC的工作原理。

Spring MVC(Model-View-Controller)是 Spring框架中用于构建 Web应用程序的模块之一,它遵循经典的MVC设计模式,将应用程序的不同方面分离,以实现更好的组织和可维护性。Spring MVC的核心思想是通过控制器来处理请求,将请求数据与业务逻辑分离,并将最终的响应结果交给视图层进行展示。

下面,我们将从架构,工作流程,核心组件分析等角度来详细分析 Spring MVC的工作原理。

Spring MVC的架构

Spring MVC的架构主要由以下几个核心组件构成:

  1. DispatcherServlet:这是Spring MVC的前端控制器,负责接收HTTP请求并将其分发给合适的处理器进行处理。它是整个Spring MVC的中央调度器。

  2. HandlerMapping:用于将请求映射到具体的处理器(Controller)上。它根据请求的URL、HTTP方法等信息来确定哪个控制器应该处理该请求。

  3. Controller:处理具体业务逻辑的组件,它接收来自DispatcherServlet的请求,调用业务服务处理后,返回一个ModelAndView对象。

  4. ModelAndView:它是Spring MVC中用于返回模型数据和视图名称的对象,控制器通过它将处理结果传递给视图层。

  5. ViewResolver:视图解析器,用于将逻辑视图名称解析成具体的视图对象(如JSP、Thymeleaf等)。

  6. View:视图用于渲染最终的结果给用户,它可以是多种形式的,比如HTML、JSON、XML等。

整体结构如下图:

springmvc-1.png

Spring MVC的工作流程

Spring MVC的请求处理流程可以分为以下几个步骤:

  1. 请求接收:用户发送一个HTTP请求到服务器,DispatcherServlet作为前端控制器接收到该请求。

  2. 请求映射:DispatcherServlet调用HandlerMapping来查找匹配的处理器(Controller)。HandlerMapping根据请求的URL、请求参数等进行匹配。

  3. 调用处理器:找到处理器后,DispatcherServlet将请求转发给具体的Controller进行处理。

  4. 业务处理:Controller执行具体的业务逻辑操作,通常会调用服务层或DAO层的方法处理数据。

  5. 返回ModelAndView:业务处理完毕后,Controller返回一个ModelAndView对象,其中包含视图名和模型数据。

  6. 视图解析:DispatcherServlet接收ModelAndView后,调用ViewResolver来解析视图名,得到具体的视图对象。

  7. 视图渲染:视图对象根据模型数据进行渲染,生成最终的输出结果。

  8. 响应返回:渲染完毕后,将结果返回给用户,整个请求处理过程结束。

DispatcherServlet详解

DispatcherServlet 是 Spring MVC 的核心组件之一,它负责处理所有进入的 HTTP 请求,并将它们分发到合适的处理器(控制器)。由于 DispatcherServlet 是一个复杂的类,这里只是摘要了 DispatcherServlet 核心源码进行分析。

1. DispatcherServlet 初始化

DispatcherServlet 在初始化过程中会加载 Spring 应用上下文,并初始化一些关键组件,如 HandlerMappingHandlerAdapterViewResolver 等。

@Override
protected void onRefresh(ApplicationContext context) {
   
    // 初始化策略模式中的各种组件
    initStrategies(context);
}

initStrategies 方法用于初始化请求处理所需的各种策略组件:

protected void initStrategies(ApplicationContext context) {
   
    initMultipartResolver(context);  // 文件上传解析器
    initLocaleResolver(context);     // 本地化解析器
    initThemeResolver(context);      // 主题解析器
    initHandlerMappings(context);    // 处理器映射
    initHandlerAdapters(context);    // 处理器适配器
    initHandlerExceptionResolvers(context); // 异常解析器
    initRequestToViewNameTranslator(context); // 视图名称翻译器
    initViewResolvers(context);      // 视图解析器
    initFlashMapManager(context);    // Flash映射管理器
}

2. 请求处理流程

DispatcherServlet 的核心请求处理逻辑在 doDispatch 方法中实现。该方法负责将请求分发到合适的处理器,并完成请求的处理。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    // 检查请求是否为文件上传请求
    if (isMultipartRequest(request)) {
   
        // 处理文件上传请求
        processedRequest = checkMultipart(request);
        multipartRequestParsed = (processedRequest != request);
    }

    // 确定请求的处理器
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
   
        noHandlerFound(processedRequest, response);
        return;
    }

    // 确定处理器适配器
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    // 处理请求并返回ModelAndView
    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    // 渲染视图
    processDispatchResult(processedRequest, response, mappedHandler, mv, null);
}

3. 处理器映射和适配

DispatcherServlet 使用 HandlerMapping 来查找合适的处理器,并使用 HandlerAdapter 来调用处理器的方法。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   
    for (HandlerMapping hm : this.handlerMappings) {
   
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
   
            return handler;
        }
    }
    return null;
}

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   
    for (HandlerAdapter ha : this.handlerAdapters) {
   
        if (ha.supports(handler)) {
   
            return ha;
        }
    }
    throw new ServletException("No adapter for handler [" + handler + "]");
}

4. 视图解析和渲染

DispatcherServlet 使用 ViewResolver 来解析视图名称并渲染视图。

protected void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                     HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
        throws Exception {
   

    if (mv != null && !mv.wasCleared()) {
   
        render(mv, request, response);
    }
}

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
   
        // 使用ViewResolver解析视图名称
        view = resolveViewName(viewName, mv.getModelInternal(), request);
    } else {
   
        view = mv.getView();
    }
    if (view != null) {
   
        // 渲染视图
        view.render(mv.getModelInternal(), request, response);
    }
}

5. 异常处理

DispatcherServlet 也具有处理异常的能力,它会使用配置的异常解析器来处理请求过程中发生的异常。

protected void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                     HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
        throws Exception {
   

    if (exception != null) {
   
        if (exceptionResolvers != null) {
   
            for (HandlerExceptionResolver resolver : this.exceptionResolvers) {
   
                ModelAndView exMv = resolver.resolveException(request, response, mappedHandler.getHandler(), exception);
                if (exMv != null) {
   
                    mv = exMv;
                    break;
                }
            }
        }
    }

    if (mv != null && !mv.wasCleared()) {
   
        render(mv, request, response);
    }
}

HandlerMapping和Controller

在 Spring MVC 框架中,HandlerMappingController 是两个非常重要的组件,它们负责请求的路由和处理,下面我们详细分析这两个组件的作用及其工作机制。

HandlerMapping

HandlerMapping 是 Spring MVC 中的一个接口,它的主要作用是根据请求的 URL、HTTP 方法等信息将请求映射到具体的处理器(通常是一个控制器方法)。HandlerMapping 的存在使得应用程序可以灵活地配置请求路径与控制器之间的映射关系。

常见的 HandlerMapping 实现

1. BeanNameUrlHandlerMapping

  • 通过 Bean 的名称来匹配 URL。
  • 适用于简单的 URL 到处理器的映射。

2. RequestMappingHandlerMapping

  • 使用 @RequestMapping 注解来定义请求路径和处理器方法之间的关系。
  • 是 Spring MVC 中最常用的映射方式,支持复杂的 URL 模式、HTTP 方法、请求参数等匹配。

3. SimpleUrlHandlerMapping

  • 通过配置文件定义 URL 到处理器的映射。
  • 适用于需要在外部配置文件中定义映射关系的场景。

HandlerMapping 的工作流程

  1. 请求到达 DispatcherServlet: 当一个请求到达 DispatcherServlet 时,DispatcherServlet 会调用 HandlerMapping 来查找处理该请求的处理器。

  2. 查找处理器HandlerMapping 接收到请求信息后,根据其实现方式(如注解、配置文件)查找与请求匹配的处理器。

  3. 返回处理器信息: 一旦找到匹配的处理器,HandlerMapping 返回一个 HandlerExecutionChain 对象,该对象包含处理器实例和相关的拦截器。

Controller

Controller 是 Spring MVC 中用于处理请求的组件。它负责接收请求参数、调用业务逻辑,并返回视图名称或响应数据。Controller 的设计使得业务逻辑与请求处理分离,便于维护和扩展。

Controller 的类型

1. 注解驱动的控制器

  • 使用 @Controller@RequestMapping 注解来定义控制器类和处理方法。
  • 是当前 Spring MVC 中最常用的控制器类型。
  • 支持多种注解来处理请求参数、路径变量、请求体等。

2. 传统的控制器接口

  • 实现 Controller 接口的类。
  • 在早期的 Spring MVC 中使用较多,现在多被注解驱动的控制器所取代。

Controller 的工作流程

  1. 接收请求:控制器方法通过 @RequestMapping 注解指定的路径接收特定的请求。

  2. 处理请求参数:使用注解如 @RequestParam@PathVariable@RequestBody 等来处理请求参数和请求体。

  3. 调用业务逻辑:控制器通常会调用服务层或业务逻辑层的方法来处理请求数据。

  4. 返回结果:控制器方法可以返回一个 ModelAndView 对象、视图名称字符串,或者直接返回数据(如 JSON、XML)。

  5. 异常处理:控制器可以通过 @ExceptionHandler 注解来处理方法中抛出的异常。

视图的渲染

在视图渲染阶段,视图对象根据Model中的数据进行渲染,生成最终的输出结果。视图的类型可以多种多样,如HTML、JSON、XML等。常见的视图技术包括:

  • JSP:传统的Java Server Pages,用于生成动态HTML。
  • Thymeleaf:现代的模板引擎,支持更强的HTML5功能。
  • FreeMarker:另一种流行的模板引擎,支持复杂的模板语法。

总结

本文,我们分析了 Spring MVC的原理,因为其涉及的内容比较多,所以在面试过程中,我们要抓大放小,先掌握其High Level的设计思想。

springmvc-1.png

在掌握了High Level的设计思想之后,我们再去分析它的几个核心组件,如 DispatcherServlet、HandlerMapping、Controller、ViewResolver等。

另外,在日常工作中,除了应付面试,我们更应该更多地去了解 SpringMVC的底层原理,可以毫不夸张地说,只要是和业务的 CRUD打交道,几乎离不开 Spring MVC,所以多了解其原理,可以帮助我们更深入地掌握它,构建出可维护、可扩展的 Web应用程序。

学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注:猿java,持续输出硬核文章。

目录
相关文章
|
3天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
9 0
|
3月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
4月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
118 0
|
4月前
|
设计模式 监控 Java
解析Spring Cloud中的断路器模式原理
解析Spring Cloud中的断路器模式原理
|
1月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
126 9
|
1月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
67 0
|
1月前
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
52 0
|
3月前
|
XML Java 数据格式
Spring5入门到实战------2、IOC容器底层原理
这篇文章深入探讨了Spring5框架中的IOC容器,包括IOC的概念、底层原理、以及BeanFactory接口和ApplicationContext接口的介绍。文章通过图解和实例代码,解释了IOC如何通过工厂模式和反射机制实现对象的创建和管理,以及如何降低代码耦合度,提高开发效率。
Spring5入门到实战------2、IOC容器底层原理