Spring MVC源码分析(三):请求分发器DispatcherServlet的设计与实现

本文主要是介绍Spring MVC源码分析(三):请求分发器DispatcherServlet的设计与实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、概述

  • DispatcherServlet是SpringMVC的一个前端控制器,是MVC架构中的C,即controller的实现,用于拦截这个web应用的所有请求,具体为在web.xml中配置这个servlet,对应的url-pattern设置为“/”,或者使用servlet3.0之后的WebApplicationInitializer来配置,在web容器启动这个应用时,会创建和初始化这个DispatcherServlet对象实例。
  • DispatcherServlet在接收到请求之后,会根据请求的uri信息,找到对应的某个controller的某个方法来处理这个请求。通常在controller对应的类中使用@Controller和@RequestMapping来唯一确定类的一个方法处理哪些uri请求,具体包括路径,http请求方法等信息。同时还需要处理主题theme,本地化locale,multipart请求,以及响应到View视图的映射。
  • 基于以上需求背景,DispatcherServlet需要定义相关的子组件来完成这些功能。由于Spring的ApplicationContext体系结构设计当中是支持层次化的,即整个spring应用包含一个root WebApplicationContext,多个子WebApplicationContext,子WebApplicationContext共享这个root WebApplicationContext。每个DispatcherServlet可以使用一个独立的,只与这个DispatcherServlet实例绑定的WebApplicationContext来创建和管理这些子组件。

二、DispatcherServlet的类结构设计

  • DispatcherServlet既是servlet规范中的一个普通的servlet,在service方法中定义对请求的处理;又通过spring的WebApplicationContext,即使用spring的beans容器机制,来管理其相关的子组件bean,并在自身中维护对spring容器中的子组件bean的引用,从而方便调用。所以在DispatcherServlet类结构设计当中,就需要考虑这两个方面的设计,如图:
    在这里插入图片描述
  • 从上图可以看出,在DispatcherServlet的类的继承体系中,从下到上依次为:DispatcherServlet -> FrameworkServlet -> HttpServletBean。

1. HttpServletBean

  • 继承于HttpServlet,实现了EnvironmentAware(注入Environment对象)和EnvironmentCapable(访问Environment对象)接口,其中Environment主要从类路径的属性文件,运行参数,@PropertySource注解等获取应用的相关属性值,以提供给spring容器相关组件访问,或者写入属性到Environment来给其他组件访问。HttpServletBean的主要作用就是将于该servlet相关的init-param,封装成bean属性,然后保存到Environment当中,从而可以在spring容器中被其他bean访问。

2. FrameworkServlet

  • 继承于HttpServletBean,因为DispatcherServlet通常包含一个独立的WebApplication,而普通的servlet则只是通过servletContext获取spring容器的root WebApplicationContext,从而从中获取相关bean,所以在DispatcherServlet类层次结构中,增加FrameworkServlet这层设计。作用就是用于获取,创建与管理,DispatcherServlet所绑定的WebApplicationContext对象,即完成WebApplicationContext的创建相关的:从contextConfigLocation获取xml或WebApplicationInitializer配置信息,根据contextClass创建WebApplicationContext,以及获取ApplicationContextInitializer来对WebApplicationContext进行初始化,最后调用refresh完成DispatcherServlet绑定的这个WebApplicationContext的创建。

3. DispatcherServlet

  • 从FrameworkServlet中获取WebApplicationContext,然后从WebApplicationContext中获取DispatcherServlet的相关功能子组件bean,然后在自身维护一个引用。实现doService方法并使用这些功能子组件来完成请求的处理和生成响应。

4. 三者的关联

  • 在HttpServletBean的init方法中,定义initServletBean模板方法,供子类实现,其中FrameworkServlet的initServletBean方法实现为创建WebApplicationContext,即调用initWebApplicationContext方法来完成WebApplicationContext的创建,并在initWebApplicationContext方法中定义onRefresh模板方法由子类实现,其中DispatcherServlet的onRefresh方法实现为从initWebApplicationContext的WebApplicationContext获取其功能子组件的bean,保存在自身的引用中。

三、DispatcherServlet的功能子组件

  • 在DispatcherServlet的onRefresh方法中,完成从WebApplicationContext获取对应的bean,赋值到自身的引用当中,源码实现如下:

    /*** This implementation calls {@link #initStrategies}.*/
    @Override
    protected void onRefresh(ApplicationContext context) {initStrategies(context);
    }/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/
    protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);
    }
    
  • DispatcherServlet中对应的bean引用如下:即核心功能组件,主要包括multipart请求处理器multipartResolver,本地化处理器localeResolver,主题(JSP的css样式)处理器themeResolver,请求URI和处理方法映射handlerMappings,请求处理器适配器handlerAdapters,请求异常处理器handlerExceptionResolvers,视图名称解析器viewNameTranslator,视图处理器viewResolvers,转发请求数据存储器flashMapManager。

    /** MultipartResolver used by this servlet. */
    @Nullable
    private MultipartResolver multipartResolver;/** LocaleResolver used by this servlet. */
    @Nullable
    private LocaleResolver localeResolver;/** ThemeResolver used by this servlet. */
    @Nullable
    private ThemeResolver themeResolver;/** List of HandlerMappings used by this servlet. */
    @Nullable
    private List<HandlerMapping> handlerMappings;/** List of HandlerAdapters used by this servlet. */
    @Nullable
    private List<HandlerAdapter> handlerAdapters;/** List of HandlerExceptionResolvers used by this servlet. */
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;/** RequestToViewNameTranslator used by this servlet. */
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;/** FlashMapManager used by this servlet. */
    @Nullable
    private FlashMapManager flashMapManager;/** List of ViewResolvers used by this servlet. */
    @Nullable
    private List<ViewResolver> viewResolvers;
    
  • 重点需要关注initHandlerMappings的实现,主要是从WebApplicationContext获取HandlerMapping接口实现类的对象实例,然后从HandlerMapping中获取请求URI和请求方法的处理器映射,其中一个重要的实现为RequestMappingHandlerMapping(包为:org.springframework.web.servlet.mvc.method.annotation),这个是处理@Controller和@RequestMapping,然后生成HandlerMethod对象实现了映射URI的(URI的信息由RequestMappingInfo维护),具体在后续文章中详细分析。

四、DispatcherServlet的请求处理

  • DispatcherServlet作为前端控制器,接收所有的请求(即从客户端发送请求到tomcat,tomcat定位到该应用,分配给DispatcherServlet),在doService方法中定义对请求的处理逻辑。doService方法对请求request进行一些加工,即添加一些attribute,包含当前DispatcherServlet的WebApplicationContext,本地化处理器localeResolver,主题处理器themeResolver等功能组件,而选择哪个controler的哪个方法来处理的逻辑,则由子方法doDispatch来实现,并且生成response响应客户端,源码如下:

    /*** Process the actual dispatching to the handler.* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters* to find the first that supports the handler class.* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers* themselves to decide which methods are acceptable.* @param request current HTTP request* @param response current HTTP response* @throws Exception in case of any kind of processing failure*/
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {// muiltpart请求处理,如果是则进行封装加工processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 获取处理这个请求的那个controler的那个方法// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 使用请求处理器适配器HandlerAdapter来定义// 请求的DispatcherServlet的统一处理格式// 即定义一种统一的模板来调用不同的Handler实现请求处理// 其中Handler由拦截器链和请求处理方法HandlerMethod组成// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 调用拦截器链各个拦截器的preHandle,进行预处理if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 请求的实际处理,返回一个ModelAndView// 其中Model表示模型,即包含数据在视图渲染中使用// View为定义到具体的JSP视图// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}// 对客户端进行响应processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
    }
    

五、总结

  • 通过以上分析可知,DispatcherServlet作为springMVC框架的一个统一前端控制器,需要接收所有发送到这个应用的请求,然后在自身启动时,已经加载好的URI和请求处理器映射,获取对应的请求处理器,由请求处理器进行实际的请求处理。
  • 所以基于此,DispatcherServlet需要多种子功能组件来完成请求处理,由于应用也可以使用多个DispatcherServlet来接收请求,为了对众多子功能组件的封装和多个DispatcherServlet的子组件的隔离性,每个DispatcherServlet使用了一个自身独立spring子容器WebApplicationContext来管理自身的子功能组件。然后共享同一个root WebApplicationContext(即WEB-INF/applicationContext.xml)来获取公用组件,如数据库连接池等。
  • 在这些子功能组件中,我们需要核心关注HandlerMappings,即请求URI和请求处理器映射这个组件的设计,即DispatcherServlet的WebApplicationContext是如何产生这个映射的,映射的设计是怎么样的,请求处理器具体是什么,如我们应用代码通常是使用@Controller和@RequestMapping来定义的,这些在底层源码是怎么实用的;当一个请求到来时,DispatcherServlet是如何从里面查找的。这些问题其实就需要到spring的IOC实现了,即spring-context和spring-beans包的实现,具体在后续文章分析。

这篇关于Spring MVC源码分析(三):请求分发器DispatcherServlet的设计与实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/957122

相关文章

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

MYSQL查询结果实现发送给客户端

《MYSQL查询结果实现发送给客户端》:本文主要介绍MYSQL查询结果实现发送给客户端方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql取数据和发数据的流程(边读边发)Sending to clientSending DataLRU(Least Rec

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

Java SWT库详解与安装指南(最新推荐)

《JavaSWT库详解与安装指南(最新推荐)》:本文主要介绍JavaSWT库详解与安装指南,在本章中,我们介绍了如何下载、安装SWTJAR包,并详述了在Eclipse以及命令行环境中配置Java... 目录1. Java SWT类库概述2. SWT与AWT和Swing的区别2.1 历史背景与设计理念2.1.

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

SpringBoot 中 CommandLineRunner的作用示例详解

《SpringBoot中CommandLineRunner的作用示例详解》SpringBoot提供的一种简单的实现方案就是添加一个model并实现CommandLineRunner接口,实现功能的... 目录1、CommandLineRunnerSpringBoot中CommandLineRunner的作用

Java死锁问题解决方案及示例详解

《Java死锁问题解决方案及示例详解》死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下... 目录1、简述死锁的四个必要条件:2、死锁示例代码3、如何检测死锁?3.1 使用 jstack3.2