都说拼多多是程序员的终点站,这篇文章,我们来分析一道它的 1面题目:聊聊Spring MVC的工作原理。
Spring MVC(Model-View-Controller)是 Spring框架中用于构建 Web应用程序的模块之一,它遵循经典的MVC设计模式,将应用程序的不同方面分离,以实现更好的组织和可维护性。Spring MVC的核心思想是通过控制器来处理请求,将请求数据与业务逻辑分离,并将最终的响应结果交给视图层进行展示。
下面,我们将从架构,工作流程,核心组件分析等角度来详细分析 Spring MVC的工作原理。
Spring MVC的架构 {#Spring-MVC的架构}
Spring MVC的架构主要由以下几个核心组件构成:
-
DispatcherServlet:这是Spring MVC的前端控制器,负责接收HTTP请求并将其分发给合适的处理器进行处理。它是整个Spring MVC的中央调度器。
-
HandlerMapping:用于将请求映射到具体的处理器(Controller)上。它根据请求的URL、HTTP方法等信息来确定哪个控制器应该处理该请求。
-
Controller:处理具体业务逻辑的组件,它接收来自DispatcherServlet的请求,调用业务服务处理后,返回一个ModelAndView对象。
-
ModelAndView:它是Spring MVC中用于返回模型数据和视图名称的对象,控制器通过它将处理结果传递给视图层。
-
ViewResolver:视图解析器,用于将逻辑视图名称解析成具体的视图对象(如JSP、Thymeleaf等)。
-
View:视图用于渲染最终的结果给用户,它可以是多种形式的,比如HTML、JSON、XML等。
整体结构如下图:
Spring MVC的工作流程 {#Spring-MVC的工作流程}
Spring MVC的请求处理流程可以分为以下几个步骤:
-
请求接收:用户发送一个HTTP请求到服务器,DispatcherServlet作为前端控制器接收到该请求。
-
请求映射:DispatcherServlet调用HandlerMapping来查找匹配的处理器(Controller)。HandlerMapping根据请求的URL、请求参数等进行匹配。
-
调用处理器:找到处理器后,DispatcherServlet将请求转发给具体的Controller进行处理。
-
业务处理:Controller执行具体的业务逻辑操作,通常会调用服务层或DAO层的方法处理数据。
-
返回ModelAndView:业务处理完毕后,Controller返回一个ModelAndView对象,其中包含视图名和模型数据。
-
视图解析:DispatcherServlet接收ModelAndView后,调用ViewResolver来解析视图名,得到具体的视图对象。
-
视图渲染:视图对象根据模型数据进行渲染,生成最终的输出结果。
-
响应返回:渲染完毕后,将结果返回给用户,整个请求处理过程结束。
DispatcherServlet详解 {#DispatcherServlet详解}
DispatcherServlet
是 Spring MVC 的核心组件之一,它负责处理所有进入的 HTTP 请求,并将它们分发到合适的处理器(控制器)。由于 DispatcherServlet
是一个复杂的类,这里只是摘要了 DispatcherServlet
核心源码进行分析。
1. DispatcherServlet 初始化
DispatcherServlet
在初始化过程中会加载 Spring 应用上下文,并初始化一些关键组件,如 HandlerMapping
、HandlerAdapter
、ViewResolver
等。
|-------------------|----------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| @Override protected void onRefresh(ApplicationContext context) { // 初始化策略模式中的各种组件 initStrategies(context); }
|
initStrategies
方法用于初始化请求处理所需的各种策略组件:
|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11
| 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
方法中实现。该方法负责将请求分发到合适的处理器,并完成请求的处理。
|------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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
来调用处理器的方法。
|------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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
来解析视图名称并渲染视图。
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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
也具有处理异常的能力,它会使用配置的异常解析器来处理请求过程中发生的异常。
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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 {#HandlerMapping和Controller}
在 Spring MVC 框架中,HandlerMapping
和 Controller
是两个非常重要的组件,它们负责请求的路由和处理,下面我们详细分析这两个组件的作用及其工作机制。
HandlerMapping {#HandlerMapping}
HandlerMapping
是 Spring MVC 中的一个接口,它的主要作用是根据请求的 URL、HTTP 方法等信息将请求映射到具体的处理器(通常是一个控制器方法)。HandlerMapping
的存在使得应用程序可以灵活地配置请求路径与控制器之间的映射关系。
常见的 HandlerMapping 实现 {#常见的-HandlerMapping-实现}
1. BeanNameUrlHandlerMapping:
- 通过 Bean 的名称来匹配 URL。
- 适用于简单的 URL 到处理器的映射。
2. RequestMappingHandlerMapping:
- 使用
@RequestMapping
注解来定义请求路径和处理器方法之间的关系。 - 是 Spring MVC 中最常用的映射方式,支持复杂的 URL 模式、HTTP 方法、请求参数等匹配。
3. SimpleUrlHandlerMapping:
- 通过配置文件定义 URL 到处理器的映射。
- 适用于需要在外部配置文件中定义映射关系的场景。
HandlerMapping 的工作流程 {#HandlerMapping-的工作流程}
-
请求到达 DispatcherServlet : 当一个请求到达
DispatcherServlet
时,DispatcherServlet
会调用HandlerMapping
来查找处理该请求的处理器。 -
查找处理器 :
HandlerMapping
接收到请求信息后,根据其实现方式(如注解、配置文件)查找与请求匹配的处理器。 -
返回处理器信息 : 一旦找到匹配的处理器,
HandlerMapping
返回一个HandlerExecutionChain
对象,该对象包含处理器实例和相关的拦截器。
Controller {#Controller}
Controller
是 Spring MVC 中用于处理请求的组件。它负责接收请求参数、调用业务逻辑,并返回视图名称或响应数据。Controller
的设计使得业务逻辑与请求处理分离,便于维护和扩展。
Controller 的类型 {#Controller-的类型}
1. 注解驱动的控制器:
- 使用
@Controller
和@RequestMapping
注解来定义控制器类和处理方法。 - 是当前 Spring MVC 中最常用的控制器类型。
- 支持多种注解来处理请求参数、路径变量、请求体等。
2. 传统的控制器接口:
- 实现
Controller
接口的类。 - 在早期的 Spring MVC 中使用较多,现在多被注解驱动的控制器所取代。
Controller 的工作流程 {#Controller-的工作流程}
-
接收请求 :控制器方法通过
@RequestMapping
注解指定的路径接收特定的请求。 -
处理请求参数 :使用注解如
@RequestParam
、@PathVariable
、@RequestBody
等来处理请求参数和请求体。 -
调用业务逻辑:控制器通常会调用服务层或业务逻辑层的方法来处理请求数据。
-
返回结果 :控制器方法可以返回一个
ModelAndView
对象、视图名称字符串,或者直接返回数据(如 JSON、XML)。 -
异常处理 :控制器可以通过
@ExceptionHandler
注解来处理方法中抛出的异常。
视图的渲染 {#视图的渲染}
在视图渲染阶段,视图对象根据Model中的数据进行渲染,生成最终的输出结果。视图的类型可以多种多样,如HTML、JSON、XML等。常见的视图技术包括:
- JSP:传统的Java Server Pages,用于生成动态HTML。
- Thymeleaf:现代的模板引擎,支持更强的HTML5功能。
- FreeMarker:另一种流行的模板引擎,支持复杂的模板语法。
总结 {#总结}
本文,我们分析了 Spring MVC的原理,因为其涉及的内容比较多,所以在面试过程中,我们要抓大放小,先掌握其High Level的设计思想。
在掌握了High Level的设计思想之后,我们再去分析它的几个核心组件,如 DispatcherServlet、HandlerMapping、Controller、ViewResolver等。
另外,在日常工作中,除了应付面试,我们更应该更多地去了解 SpringMVC的底层原理,可以毫不夸张地说,只要是和业务的 CRUD打交道,几乎离不开 Spring MVC,所以多了解其原理,可以帮助我们更深入地掌握它,构建出可维护、可扩展的 Web应用程序。