写点什么

Spring 核心原理分析之 MVC 九大组件(1)

作者:Tom弹架构
  • 2021 年 12 月 22 日
  • 本文字数:3176 字

    阅读完需:约 10 分钟

本文节选自《Spring 5 核心原理》

1 什么是 Spring MVC

Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。Spring MVC 角色划分清晰,分工明细。由于 Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架,最热门的开发技能。首先从一个由 Spring 提供的 DispatcherServlet 开始,重写了 Serlvet 的 init()方法、service()方法和 destroy()方法,SpringMVC 九大组件在 DispatcherServlet 的 init()方法中初始化,在 service()方法中执行。下面,我们先来看 Spring MVC 九大组件的初始化。

2 SpringMVC 九大组件名称解释

Spring MVC 九大组件在 DispatcherServlet 的 init()方法中初始化,下面我详细介绍一下 Spring MVC 九大组件的名称和作用。



具体详细介绍如下:

2.1 MultipartResolver

MultipartResolver 是一个大家很熟悉的组件,用于处理上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实现。MultipartHttpServletRequest 可以通过 getFile()方法直接获得文件。如果上传多个文件,还可以调用 getFileMap()方法得到 Map< FileName, File> 这样的结构。MultipartResolver 的作用就是封装普通的请求,使其拥有文件上传的功能。

2.2 LocaleResolver

ViewResolver 组件的 resolveViewName()方法需要两个参数,一个是视图名,另一个就是 Locale。参数 Locale 是从哪来的呢?这就是 LocaleResolver 组件要做的事。LocaleResolver 用于从请求中解析出 Locale,比如在中国 Locale 当然就是 zh-CN,用来表示一个区域。这个组件也是 i18n 的基础。

2.3 ThemeResolver

从名字便可看出,ThemeResolver 组件是用来解析主题的。主题就是样式、图片及它们所形成的显示效果的集合。Spring MVC 中一套主题对应一个 properties 文件,里面存放着与当前主题相关的所有资源,如图片、CSS 样式等。创建主题非常简单,只需准备好资源,然后新建一个“主题名.properties”并将资源设置进去,放在 classpath 下,之后便可以在页面中使用了。Spring MVC 中与主题有关的类有 ThemeResolver、ThemeSource 和 Theme。ThemeResolver 负责从请求中解析出主题名,ThemeSource 则根据主题名找到具体的主题,其抽象也就是 Theme,可以通过 Theme 来获取主题和具体的资源。

2.4 HandlerMapping

HandlerMapping 是用来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是方法。比如,标注了 @RequestMapping 的每个方法都可以看成一个 Handler。Handler 负责实际的请求处理,在请求到达后,HandlerMapping 的作用便是找到请求相应的处理器 Handler 和 Interceptor。

2.5 HandlerAdapter

从名字上看,HandlerAdapter 是一个适配器。因为 Spring MVC 中 Handler 可以是任意形式的,只要能够处理请求便可。但是把请求交给 Servlet 的时候,由于 Servlet 的方法结构都是 doService(HttpServletRequest req, HttpServletResponse resp)形式的,要让固定的 Servlet 处理方法调用 Handler 来进行处理,这一步工作便是 HandlerAdapter 要做的事。

2.6 HandlerExceptionResolver

从组件的名字上看,HandlerExceptionResolver 是用来处理 Handler 产生的异常情况的组件。具体来说,此组件的作用是根据异常设置 ModelAndView,之后交给渲染方法进行渲染,渲染方法会将 ModelAndView 渲染成页面。不过要注意,HandlerExceptionResolver 只用于解析对请求做处理阶段产生的异常,渲染阶段的异常不归它管,这也是 Spring MVC 组件设计的一大原则—分工明确、互不干涉。

2.7 RequestToViewNameTranslator

RequestToViewNameTranslator 组件的作用是从请求中获取 ViewName。因为 ViewResolver 根据 ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName,便要通过这个组件来从请求中查找 ViewName。

2.8 ViewResolver

ViewResolver 即视图解析器,相信大家对这个组件应该很熟悉了。通常在 Spring MVC 的配置文件中,都会配上一个实现类来进行视图解析。这个组件的主要作用是将 String 类型的视图名和 Locale 解析为 View 类型的视图,只有一个 resolveViewName()方法。从方法的定义可以看出,Controller 层返回的 String 类型的视图名 viewName 最终会在这里被解析成为 View。View 是用来渲染页面的,也就是说,它会将程序返回的参数和数据填入模板中,生成 HTML 文件。ViewResolver 在这个过程中主要做两件大事:ViewResolver 会找到渲染所用的模板(第一件大事)和所用的技术(第二件大事,其实也就是找到视图的类型,如 JSP)并填入参数。默认情况下,Spring MVC 会为我们自动配置一个 InternalResourceViewResolver,是针对 JSP 类型视图的。

2.9 FlashMapManager

说到 FlashMapManager 组件,得先说一下 FlashMap。FlashMap 用于重定向时的参数传递,比如在处理用户订单时,为了避免重复提交,可以处理完 post 请求后重定向到一个 get 请求,这个 get 请求可以用来显示订单详情之类的信息。这样做虽然可以规避用户重新提交订单的问题,但是在这个页面上要显示订单的信息,这些数据从哪里获取呢?因为重定向是没有传递参数这一功能的,如果不想把参数写进 URL(其实也不推荐这么做,除了 URL 有长度限制,把参数都直接暴露也不安全),那么就可以通过 FlashMap 来传递。只需要在重定向之前将要传递的数据写入请求(可以通过 ServletRequestAttributes.getRequest()方法获得)的属性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,这样在重定向之后的 Handler 中 Spring 就会自动将其设置到 Model 中,在显示订单信息的页面上就可以直接从 Model 中获得数据。FlashMapManager 就是用来管理 FlashMap 的。

3 Spring MVC 关键组件的执行流程

Spring MVC 九大组件的执行在 DispatcherServlet 的 service()方法中完成。在这里,我重点介绍几个关键组件 HandlerMapping、HandlerAdapter、ViewResolver 在 service()方法中的执行流程,具体调用分为以下几个步骤:


1、HandlerMapping 回到调用 HandlerAdapter


2、HandlerAdapter 会返回 ModelAndView


3、ModelAndView 根据用户传入参数得到 ViewResolvers


4、ViewResolvers 会将用户传入的参数封装为 View,交给引擎进行渲染。


下面给大家分享一张 Spring MVC 关键组件的执行流程图,以帮助大家更好地理解:



注意:上图中有大家最熟悉的两个类:ModelAndView 和 View 类并不属于 Spring MVC 九大组件之列。

4 Spring MVC 优化建议

前面我们已经对 Spring MVC 的工作原理和源码进行了分析,在这个过程中有几个优化点。


1. Controller 如果能保持单例模式,尽量使用单例模式


这样可以减小创建对象和回收对象的开销。也就是说,如果 Controller 的类变量和实例变量可以以方法形参声明就尽量以方法形参声明,不要以类变量和实例变量声明,这样可以避免线程安全问题。


2. 处理请求的方法中的形参务必加上 @RequestParam 注解


这样可以避免 Spring MVC 使用 asm 框架读取.class 文件获取方法参数名。即便 Spring MVC 对读取出的方法参数名进行了缓存,如果能不读取.class 文件当然更好。


3. 缓存 URL


在阅读源码的过程中,我们发现 Spring MVC 并没有对处理 URL 的方法进行缓存,也就是说,每次都要根据请求 URL 去匹配 Controller 中的方法的 URL,如果把 URL 和方法的关系缓存起来,会不会带来性能上的提升呢?不幸的是,负责解析 URL 和方法对应关系的 ServletHandlerMethodResolver 是一个私有的内部类,不能直接通过继承该类增强代码,必须在代码后重新编译。当然,如果将 URL 缓存起来,必须考虑缓存的线程安全问题。


关注微信公众号『 Tom 弹架构 』回复“Spring”可获取完整源码。


本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom 弹架构 』可获取更多技术干货!


原创不易,坚持很酷,都看到这里了,小伙伴记得点赞、收藏、在看,一键三连加关注!如果你觉得内容太干,可以分享转发给朋友滋润滋润!

发布于: 2021 年 12 月 22 日
用户头像

Tom弹架构

关注

不只做一个技术者,更要做一个思考者 2021.10.22 加入

畅销书作者,代表作品:《Spring 5核心原理》、《Netty 4核心原理》、《设计模式就该这样学》

评论

发布
暂无评论
Spring核心原理分析之MVC九大组件(1)