文档章节

Spring-MVC核心架构介绍

x
 xxm1995
发布于 07/12 11:04
字数 3048
阅读 8
收藏 0

一,核心流程

img

  1. 第一步:发起请求到前端控制器(DispatcherServlet)中的doDispatch方法,然后委托给doDispatch方法进行处理。
  2. 第二步:之后会请求getHandler方法,通过HandlerMapping查找 Handler(根据配置、注解进行查找)
  3. 第三步:查找到Handler之后,会把请求封装成HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象)并返回。
  4. 第四步:然后通过getHandlerAdapter方法获取支持处理这种handler的处理器适配器HandlerAdapter
  5. 第五步:获取到的处理器适配器HandlerAdapter,将会根据适配的结果去执行Handler,通常会是由RequestMappingHandlerAdapter#handleInternal进行执行。
  6. 第六步:Handler执行完成给适配器返回ModelAndView,这里Handler执行的通常是我们自己编写的controller实现。
  7. 第七步:处理器适配器向前端控制器返回ModelAndViewModelAndView是springmvc框架的一个底层对象,包括 Model和view)
  8. 第八步:前端控制器请求视图解析器去进行视图解析,通过这种策略很容易更换其他视图技术,比如thymeleaf、json等,只需要更改视图解析器即可
  9. 第九步:视图解析器向前端控制器返回View
  10. 第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
  11. 第十一步:前端控制器向用户响应结果

二,核心组件

1,DispatcherServlet

DispatcherServlet是Spring MVC框架的中央处理器,它负责拦截所有请求,并分发到各控制器;同时提供其他web应用开发所需要的功能。DispatcherServlet是个Servlet(它继承自HttpServlet基类),不过能做的比这更多。它与Spring IoC容器做到了无缝集成,这意味着,Spring提供的任何特性,在Spring MVC中你都可以使用。

DispatcherServlet使用了特殊的bean来处理请求、渲染视图等,这些特定的bean是框架的一部分,依赖于这些特殊的bean来进行它的初始化。Spring MVC维护了一个默认的bean列表,如果你没有进行特别的配置,框架将会使用这些默认的bean。

DispatcherServlet 细分之后,可以整理出三个功能:

  • 截获 HTTP 请求,并交由 Spring MVC 框架处理
  • 处理调用关系
  • 初始化并装配 Spring MVC 的各个组件

2,重要组件Bean列表

组件 Bean 类型说明
HandlerMapping处理器映射。它会根据某些规则将进入容器的请求映射到具体的处理器以及一系列前处理器和后处理器(即处理器拦截器)上。具体的规则视HandlerMapping类的实现不同而有所不同。其最常用的一个实现支持你在控制器上添加注解,配置请求路径。当然,也存在其他的实现。
HandlerAdapter处理器适配器。拿到请求所对应的处理器后,适配器将负责去调用该处理器,这使得DispatcherServlet无需关心具体的调用细节。比方说,要调用的是一个基于注解配置的控制器,那么调用前还需要从许多注解中解析出一些相应的信息。比如,调用注解实现的 Controller 需要解析其关联的注解. HandlerAdapter的主要目的是为了屏蔽与 DispatcherServlet 之间的诸多细节。
HandlerExceptionResolver处理器异常解析器,它负责将捕获的异常映射到不同的视图上去,此外还支持更复杂的异常处理代码。
ViewResolver视图解析器,从处理器(Handler)返回字符类型的逻辑视图名称解析出实际的 View 对象,该对象将渲染后的内容输出到HTTP 响应中。
MultipartResolver解析multi-part的传输请求,比如支持通过HTML表单进行的文件上传等。

3,主要组件

3.1 处理器映射(HandlerMapping)

职责: HandlerMapping的作用是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与 HandlerInterceptor(拦截器)封装到 HandlerExecutionChain对象(处理程序执行链,由处理程序对象和处理程序拦截器组成)中并返回。

参与时机: 在请求到达 DispatcherServlet#doDispatch的时候,会根据当前请求来确定处理程序,详细流程如下:

1,首先调用getHandler方法,从容器中取出所有 HandlerMapping 接口实例并遍历,让 HandlerMapping 接口的实例根据自己实现类的方式去尝试查找 Handler

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        //...省略
        // 查找给定请求的处理程序
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

2,在 HandlerMapping 接口的内部只有一个方法,从实现类 AbstractHandlerMapping 中的方法可以看出,会首先根据request获取到 handler 对象,然后通过request和 handler 来获取 HandlerExecutionChain 处理程序执行链。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    //...省略
    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    //...省略
    // 获取 处理程序拦截器 HandlerInterceptor并进行组装,
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    return executionChain;
}

3,getHandlerExecutionChain方法首先会将之前获取到 handler 装入 HandlerExecutionChain 中,然后通过遍历 处理程序拦截器 将符合条件的拦截器添加到 HandlerExecutionChain 处理程序执行链中并返回。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    //获取拦截器
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

4,最终获取到HandlerExecutionChain对象后,交由 HandlerAdapter (处理器适配器)进行下一步的处理。

3.2 处理器适配器(HandlerAdapter)

职责: Spring MVC的handler(ControllerHttpRequestHandler等)有多种实现方式,例如继承Controller的,基于注解控制器方式的,HttpRequestHandler方式的。由于实现方式不一样,调用方式就不确定了。 如果正常编写调用,就需要使用多个if else判断instance of,再添加实现方式,就需要修改源码,不符合对扩展开放,对修改关闭原则,所以Spring MVC提供了处理器适配器(HandlerAdapter),屏蔽了各种handler的差异,以一种统一的方式进行处理。

​ 在HandlerAdapter中有三个方法,分别是supportshandlegetLastModified

public interface HandlerAdapter {
    /**
     * 判断是否支持传入的handler
     */
    boolean supports(Object handler);
    /**
     * 使用给定的handler处理请求
     */
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    /**
     * 返回上次修改时间,可以返回-1表示不支持
     */
    long getLastModified(HttpServletRequest request, Object handler);
}

HandlerAdapter是一个接口,有多种实现方式,不过在我们平时使用中,一般只会用到RequestMappingHandlerAdapter这种实现,它应该是目前springMVC主要采用的实现,针对方法级的映射匹配处理。

RequestMappingHandlerAdapter继承自AbstractHandlerMethodAdapter抽象类,而AbstractHandlerMethodAdapterHandlerAdapter接口的抽象实现。

继承关系:HandlerAdapter-->AbstractHandlerMethodAdapter-->RequestMappingHandlerAdapter

参与时机: 当一个请求被HandlerMapping处理结束,来到处理器适配器进行处理时:

1,首先到AbstractHandlerMethodAdapter中的handle方法中,

2,然后在这个方法中调用抽象方法handleInternal

3,此时会调用实现类RequestMappingHandlerAdapter中的handleInternal方法实现,

4,在这个方法中最重要的就是invokeHandlerMethod,它会调用我们自己编写Controller中的RequestMapping方法进行处理,以及其他配置的处理,得到处理结果后执行下一步。

protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//...省略
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    //...省略
    return mav;
}

3.3 视图解析器(ViewResolver)

职责: 在处理适配器处理完请求后,就会到视图解析器(ViewResolver)进行下一步的处理,Spring MVC中所有控制器的处理器方法都必须返回一个逻辑视图的名字,无论是显式返回(比如返回一个StringView或者对象)还是隐式返回(比如基于约定的返回)。Spring中的视图由一个视图名标识,并由视图解析器来渲染。

其余待补充

4,其他组件

4.1 拦截器(Interceptors)

职责: 可以配置处理器拦截器HandlerInterceptors或web请求拦截器WebRequestInterceptors等拦截器,并配置它们拦截所有进入容器的请求,或限定到符合特定模式的URL路径。这个没什么可以说的,大家也都熟,我们对Spring MVC进行功能扩展时经常会用到它。

参与时机: 在处理器映射进行Handle装配的时候,会把拦截器给组装成列表,然后在 处理器适配器 进行处理的时候,会按照设置对拦截器进行执行,请求到来拦截器的执行在下述REST相关组件之前,返回处理在下述REST相关组件之后。

使用方法:对SpringMVC进行扩展 中的介绍

4.2 处理器异常解析器(HandlerExceptionResolver)

职责: 对出现异常的请求进行处理,这个咱们公司已经使用了统一异常拦截方式( @controlleradvice+@ExceptionHandler),对这个已经不再使用,没有介绍的必要。

参与时机: 当请求出现异常时。

使用方法:

4.3 具体处理请求控制器(Controller/Handler)

职责: 自己编写的controller就是处理请求的处理器,这个不需要介绍,大家天天用。

参与时机: 在处理器适配器进行时,会匹配到我们自己编写的请求处理器,进行处理。

使用方法: 自己编写,使用@Controller@xxxMapping等注解进行声明。

4.4 文件上传(MultipartResolver)

待补充

5,REST相关组件

5.1 处理方法参数解析器(HandlerMethodArgumentResolver)

职责: 用于 HTTP 请求中解析 HandlerMethod 参数内容,处理成Handler可用的参数数据,

参与时机: 在请求到来,进行到要对进行参数处理的时候,就会在方法参数解析器列表查找匹配的处理器,对参数进行处理。

使用方法:对SpringMVC进行扩展 中的介绍

5.2 处理方法返回值解析器(HandlerMethodReturnValueHandler)

职责: 用于 HandlerMethod 返回值解析为 HTTP 响应内容

参与时机: 当方法处理完毕后,得到返回值之后,就会在方法返回值解析器列表中查找匹配的处理器,针对返回值进行处理。

使用方法:对SpringMVC进行扩展 中的介绍

5.3 HTTP消息转换器(HttpMessageConverter)

职责: HTTP 消息转换器,用于反序列化 HTTP 请求或序列化响应。这个组件是方法级别的,

参与时机: 在请求到来时和处理完毕进行返回的时候,可以数据进行转换和处理,对请求数据进行处理的时机在 处理方法参数解析器 之前,对返回值进行处理时在方法返回值解析器之前。

使用方法:对SpringMVC进行扩展 中的介绍

6,部分组件运行先后顺序

  1. 接收到一个请求
  2. 拦截器进行请求拦截
  3. HTTP消息转换器进行请求处理
  4. 方法参数解析器进行处理
  5. 用户编写的控制器Controller进行处理
  6. 方法返回值解析器进行返回值处理(待确认)
  7. HTTP消息转换器进行返回值处理(待确认)
  8. 拦截器进行返回值拦截处理(待确认)
  9. 返回请求结果

三、常用注解(了解即可)

请求和响应相关

注解说明Spring Framework 版本
@RestController等效于 @Controller + @ResponseBody4.0 +
@RequestMapping应用控制器映射注解声明2.5+
@xxxMapping等效于 @RequestMapping(method =RequestMethod.XXX)4.3+
@RequestParam获取请求参数2.5+
@RequestHeader获取请求头3.0+
@PathVariable获取请求路径变量3.0+
@RequestBody获取完整请求主体内容3.0+
@ResponseBody响应主体声明2.5+
@PostConstruct会在依赖注入完成后被自动调用JavaEE注解

© 著作权归作者所有

x
粉丝 0
博文 24
码字总数 12971
作品 0
菏泽
私信 提问
Spring MVC 到 Spring Boot 的简化之路

背景 从Servlet技术到Spring和Spring MVC,开发Web应用变得越来越简捷。但是Spring和Spring MVC的众多配置有时却让人望而却步,相信有过Spring MVC开发经验的朋友能深刻体会到这一痛苦。因为...

别打我会飞
2018/10/21
203
0
从Spring MVC 到 Spring BOOT的简化道路

背景 从Servlet技术到Spring和Spring MVC,开发Web应用变得越来越简捷。但是Spring和Spring MVC的众多配置有时却让人望而却步,相信有过Spring MVC开发经验的朋友能深刻体会到这一痛苦。因为...

微笑向暖wx
2018/11/13
73
0
系列一、SpringMVC概念及测试案例

三层架构和MVC概念 一.、三层架构概念 咱们开发服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序,使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构 ...

我叫小糖主
05/27
19
0
新书一周销量破千,这本Spring图书为何如此受Java程序员追捧

适读人群 :超强实战技术升级指南,囊括源码、设计思想、架构思维、实战案例、开发经验,Java架构师成长必备;本书基于Spring 5开发的架构师实战指南,几乎涵盖在Spring应用中可能遇到的所有...

Java架构
07/31
0
0
Spring Portlet MVC开发入门示例

本文通过一个简单实例介绍一下Spring 的Portlet MVC框架. Spring Portlet MVC和其Web MVC可以说是如出一辙,只是在Web MVC中处于核心的DispatcherServlet在Portlet MVC中换成了DispatcherPor...

晨曦之光
2012/04/25
905
0

没有更多内容

加载失败,请刷新页面

加载更多

Taro 兼容 h5 踩坑指南

最近一周在改造 公羊阅读🐏 Taro 版本适配 h5 端,过程中改改补补,好不酸爽。 本文记录📝遇到的问题,希望为有相同需求的哥们👬节约点时间。 Taro 版本:1.3.9。 client_mobile_taro...

dkvirus
今天
4
0
Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
今天
3
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
今天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
今天
19
0
java数据类型

基本类型: 整型:Byte,short,int,long 浮点型:float,double 字符型:char 布尔型:boolean 引用类型: 类类型: 接口类型: 数组类型: Byte 1字节 八位 -128 -------- 127 short 2字节...

audience_1
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部