-
2022-01-23 10:51:35
流程图如下:
执行流程分析如下:
1.浏览器提交请求到中央调度器。2. 中央调度器直接将请求转给处理器映射器。3. 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。4. 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。5. 处理器适配器调用执行处理器。6. 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。7. 处理器适配器直接将结果返回给中央调度器。8. 中央调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象。9. 视图解析器将封装了的视图对象返回给中央调度器10. 中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。11. 中央调度器响应浏览器。更多相关内容 -
SpringMVC执行流程
2022-04-12 20:19:32SpringMVC初步认识,关于原理还需深入学习目录
1. 什么是SpringMVC
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术、Velocity、Tiles、iText和POI。Spring MVC 框架并不知道使用的视图,所以不会强迫开发者只使用 JSP 技术。Spring MVC 分离了控制器、模型对象、过滤器以及处理程序对象的角色,这种分离让它们更容易进行定制。
2. SpringMVC的特点
- 轻量级,简单易学
- 高效:基本请求相应的MVC框架
- 与Spring兼容特别好,无缝衔接
- 预定大于配置
- 功能强大
- 简单灵活
Spring的web框架围绕DispatcherServlet 设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;正因为SpringMVC好 , 简单 , 便捷 , 易学 , 天生和Spring无缝集成(使用SpringIoC和Aop) , 使用约定优于配置 . 能够进行简单的junit测试 . 支持Restful风格 .异常处理 , 本地化 , 国际化 , 数据验证 , 类型转换 , 拦截器 等等…所以我们要学习 。最重要的一点还是用的人多 , 使用的公司多。
3. SpringMVC核心控件
- 前端控制器(DispatcherServlet):对其他组件进行解耦,这样就增加了组件的可扩展性 无需开发直接配置
- 处理器映射器(HandlerMapping):无需开发,直接用,作用见上面
- 处理器适配器(HandlerAdapter):无需开发
- 处理器(Handler):需要开发,方式很多,也就是编写的 xxxController
- 视图解析器(ViewResolver):无需开发
- 视图(View):需要开发
4. SpringMVC执行流程
5. 常用注解
- @Controller 标注在类上,表示这是一个Controller层组件
- @Service 标注在类上,表示这是一个Service层组件
- @Repository 标注在类上,表示这是一个Dao持久层组件
- @RequestMapping 标注在类或方法上,用来映射Web请求(访问路径和参数)
- @ResponseBody 标注在返回值或者方法上,表示支持将返回值放在response体内,返 回json数据
- @RequestBody 标注在参数前,表示允许request的参数在request体中,而不是在直 接链接在地址后面
- @PathVariable 标注在参数前,用来接收路径参数
- @RestController 标注在类上,组合注解,组合了@Controller和@ResponseBody
6. 编写SpringMVC程序的步骤
- 新建一个Moudle , springmvc-hello , 添加web的支持!
- 导入了SpringMVC 的依赖!
- 配置web.xml , 注册DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--注册DispatcherServlet--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 这里有三种配置url-pattern方案 1、*.do:后缀为.do的请求才能够访问到该servlet[用这个] 2、/ :所有请求都能够访问到该servlet(除jsp),包括静态请求(处理会有问题,不用) 3、/* :有问题,因为访问jsp也会到该servlet,而访问jsp时,我们不需要这样,也不用 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
4. 编写SpringMVC 的 配置文件!名称:springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--添加处理器映射器--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!--添加处理器适配器--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!--添加视图解析器--> <!--视图解析器:DispatcherServlet给他的ModelAndView--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀--> <property name="suffix" value=".jsp"/> </bean> <!-- 使用注解的handle,则需要配置组件扫描器,加载handler base-package:指定要扫描的包 --> <context:component-scan basepackage="com.johong.springmvc.controller</context:component-scan> </beans>
5. 编写我们要操作业务Controller ,有三种方式,一是实现Controller接口,二是使用注解(掌握注解方式),三是实现HttpRequestHandler接口
6. 编写视图页面
-
SpringMVC 执行流程
2022-04-22 00:12:30springMVC(javaweb 开发框架) 1、MVC三层架构:模型(service、dao)、视图(JSP等)、控制器(Controller) 什么是mvc? * MVC 是模型、视图、控制器的简写,是一种软件设计规范 * 是将业务逻辑、数据、显示分离的...springMVC(javaweb 开发框架)
1、MVC三层架构:模型(service、dao)、视图(JSP等)、控制器(Controller)
什么是mvc?
* MVC 是模型、视图、控制器的简写,是一种软件设计规范
* 是将业务逻辑、数据、显示分离的方法来组织代码
* MVC主要的作用就是降低了控制器(Controller)和视图 (View) 之间的双向耦合度
* MVC不是一种设计模式、MVC是一种架构模式。当然不同的MVC存在着差异
1
2
3
4
Model(数据模型):提供要展示的数据。因此包含数据和行为、可以认为是领域模型或者JavaBean组件,不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(Service)。也就是模型提供了模型数据查询和模型数据的状态更新等,包括业务数据和义务。**View(视图):**负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
**Controller(控制器):**接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作
最典型的MVC 就是: JSP + Servlet + JavaBean
任何项目里面的架构是演变过来的。不是直接搭建好的!!!
1.2、Model1时代
在web早期的开发中,通常采用的都是Model1。
Model1中,主要分为两层,视图层和模型层。Model1优点:架构简单,比较适合小型项目开发;
Model1缺点:JSP职责不单一,职责过重,不便于维护;
1.3、Model2时代
Model2把一个项目分成三部分,包括视图、控制、模型。
1.用户发请求
2.Servlet接收请求数据,并调用对应的业务逻辑方法
3.业务处理完毕,返回更新后的数据给servlet
4.servlet转向到JSP,由JSP来渲染页面
5.响应给前端更新后的页面职责分析:
Controller:控制器
1.取得表单数据
2.调用业务逻辑
3.转向指定的页面Model:模型
1.业务逻辑
2.保存数据的状态View:视图
1.显示页面
Model2这样不仅提高的代码的复用率与项目的扩展性,且大大降低了项目的维护成本。Model 1模式的实现比较简单,适用于快速开发小规模项目,Model1中JSP页面身兼View和Controller两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。Model2消除了Model1的缺点。传统的javaweb开发:
一个请求资源路径(url) 对应一个控制器(Controller) ,每次请求之后都会自动根据所请求的资源找到对应的Servlet类,执行相应的业务。SpringMVC 开发(DispatcherServlet):
和传统的开发模式不同,SpringMVC 底层有一个核心对象:DispatcherServlet 前端控制器(分发器),使用了SpringMVC 框架之后,所有的请求都会执行 DispatcherServlet 这个对象,不再去直接执行对应的Controller,而是先通过 DispatcherServlet 前端控制器找到该请求路径(URL) 对应的控制器,前端控制器再去调用该控制器执行具体业务。官网SpringMVC的原理如下图所示:
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。SpringMVC执行流程深入理解:
实线表示为框架帮我们实现, 虚线表示需要我们自己来实现!!!!!!!
springmvc 中的 DispatcherServlet 请求路径一般设置为 “/” 项目的根路劲,只要在项目中发送请求就 会调用 该DispatcherServlet
Springmvc 在执行的过程中,在调用指定的组件的时候。会先进入指定的拦截器 !!! 1、用户在前端浏览器上发送请求到前端控制器DispatcherServlet,如果是第一次请求的会先创建该前端控制器对象(并且只会加载一次,servlet是伪单例的(构造器没有私有化))再去执行 init()方法,从spring容器中按照类型注入的方式来对 前端控制器中的组件来完成依赖注入,组件依赖注入完毕之后再去执行 doService()方法。如果不是第一次请求的话,则直接执行doService()方法将用户具体请求的url资源名,传给处理器映射器
2、处理器映射器 根据用户请求的url地址去springmvc 自己的容器中去映射对应的控制器,将映射结果封装成 目标方法 返回给前端控制器(DispatcherServlet),前端控制器将目标方法 传给 处理器适配器 再去容器中适配对应的控制器(在调用对应的控制器之前会进行一次拦截)
3、控制器根据用户的具体请求,去spring的容器中获取对应的业务逻辑层对象查询数据。
4、业务层对象将查询出的模型数据返回给控制器
5、控制器再将模型数据 返回给 处理器适配器
6、处理器适配器接收到模型数据之后,先判断响应结果是 异步请求还是同步请求,(有没有@ResponseBody注解)如果是异步请求,直接将JSON数据写回给浏览器,如果是转发的话 就将 模型数据到 模型视图对象(ModelAndView)中去,模型视图对象主要的就是封装渲染数据和展示页面的,将封装好的模型视图对象(ModelAndView)传给前端控制器(DispatcherServlet)(在传给前端控制器之前在进行一次拦截)
7、前端控制器(DispatcherServlet)再将模型视图对象传给视图层进行解析
8、视图层解析模型视图对象,拼接字符串获取的需要渲染的视图,并将数据渲染到该视图上去、再返回给前端控制器
9、前端控制器将视图解析器返回的页面视图响应给浏览器端呈现给用户
10、在springmvc整个的执行过程中只要发生了异常,就会进入自定义的全局异常处理类(ControllerAdvice ExceptionHandler)
在前端浏览器上第一次请求我们的 DispatchServlet 前端控制器(核心组件),会创建该DispatchServlet对象的实例,再执行 DispatchServlet 中的 init ()方法 , 从 spring 容器中 按照 类型注入 来获取 DispatchServlet 中的属性对应的组件 来进行 依赖注入 !!!
如果不是 第一次请求的话,各大 组件依赖 注入完毕,直接执行 doService()方法 来完成后续操作!!!
SpringMvc 底层也有自己的一个容器:WebXmlApplicationContext ,和 spring的 ApplicationContext 容器是父子关系,
SpringMVC 的容器 是继承了 Spring容器的,spring容器是父容器,springmvc容器是子容器!!!
springmvc 在需要使用到某个功能组件的时候,先去自己的WebXmlApplicationContext容器中去找,如果没有则去spring容器中去找
springmvc可以获取 spring容器中的bean,而spring则无法获取 springmvc容器中的bean !!!!
一般的话像 Controller 层对象 一般都是存放在 springmvc 的容器中 来共 springmvc 中的 处理器适配去 去调用!!!
Service 层 和 Dao 层对象,一般则是放在 spring容器中,因为 像一些事务的处理呀和 mybatis 核心对象的生成 不是加上注解之后就
会立即生效,而是先生成代理对象,一般这些代理对象一般都在 spring 容器中注册!!!
-
阿昌教你看懂SpringMVC执行流程
2021-12-19 17:53:24阿昌教你看懂SpringMVC执行流程 一、前言 Hello呀!!!阿昌又来也 ╰(°▽°)╯!!! SpringMVC的执行流程大家应该都挺熟悉的,但是真的去debug源码的人应该算少数,这里阿昌一直都想自己记录一下debug-SpringMVC的...阿昌教你看懂SpringMVC执行流程
一、前言
Hello呀!!!阿昌又来也 ╰(°▽°)╯!!!
SpringMVC的执行流程大家应该都挺熟悉的,但是真的去debug源码的人应该算少数,这里阿昌一直都想自己记录一下debug-SpringMVC的执行流程源码,做一下总结,今天终于有机会记录一下SpringMVC执行流程
同样我还是建议打开源码一起debug看!!!
1、流程图
- 执行图
更详细一点:
2、基于版本
SpringBoot
:2.4.13、前置的测试代码
这里debug只涉及到
controller
和Interceptor拦截器
,且端口在8080
(๑•̀ㅂ•́)و✧- TestController控制器
@RequestMapping("/test") @RestController public class TestController { @GetMapping("/123") public String test(@RequestParam String name, ServletResponse response){ System.out.println("name="+name); System.out.println("response:"+response.isCommitted()); return "我是结果"; } }
-
MyInterceptor拦截器
拦截器就涉及到了3个执行方法的
执行顺序
- preHandle()
- postHandle()
- afterCompletion()
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyInterceptor.preHandle"); System.out.println("response:"+response.isCommitted()); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("MyInterceptor.postHandle"); System.out.println("response:"+response.isCommitted()); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyInterceptor.afterCompletion"); System.out.println("response:"+response.isCommitted()); } }
二、正文
这里我们启动服务,并用浏览器请求:
http://localhost:8080/test/123?name=阿昌
(。・∀・)ノ゙我们都知道对于SpringBoot中是自带Tomcat服务器的组件的,当一个请求发来,会被Tomcat处理,并转交给SpringMVC中的
DispatcherServlet类
来做接下来的处理,他在SpringMVC中非常的重要,起着流程执行骨架的作用。1、doDispatch
那首先Tomcat会经过流转调用去执行
DispatcherServlet.doDispatch()
方法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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 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; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 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 afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
2、getHandler
经过一些的初始化后,首先会去执行
getHandler()
方法,去寻找对应可以去处理这个请求的mappedHandler
-
mappedHandler对应的类型是HandlerExecutionChain
HandlerExecutionChain
就是拦截器链
- getHandler()
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) {//遍历预设的handlerMappings for (HandlerMapping mapping : this.handlerMappings) { //看看哪个能处理这次的请求 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { //找到了就返回这个handler(其实这个handler被包装了一层,HandlerExecutionChain) return handler; } } } return null; }
- mapping.getHandler(request)
mapping.getHandler(request)拿到的是被包装后的handlerMapping,也就是
HandlerExecutionChain
- getHandlerExecutionChain
- handlerMapping处理器
- 对应的拦截器
- 这次的请求request
封装成
HandlerExecutionChain
- getHandlerInternal
获取handlerMapping
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { ///这次请求的uri为:test/123 String lookupPath = initLookupPath(request);//获取这次请求的uri this.mappingRegistry.acquireReadLock(); try { //根据这次请求和这个uri,判断获取对应能处理的HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
4、小总结
执行这里就会遍历所有
this.handlerMappings
,获取请求的uri和请求,拿到对应能够处理这次请求的handlerMapping
,并将拿到:↓- handlerMapping处理器
- 对应的拦截器
- 这次的请求request
包装成一个
HandlerExecutionChain
。
5、getHandlerAdapter()
上面我们获取到的对应的
HandlerExecutionChain拦截器链
(处理器handlermapping+拦截器链+这次请求)。接下来就要获取对应这个handlermapping对应的的
适配器HandlerAdapter
- getHandlerAdapter
里面的逻辑也很简单,遍历所有的
handlerAdapter
,看看哪个可以处理个handlerMapping,找到后返回
6、判断如果是get请求,是否被修改
7、applyPreHandle()
这里就是上面流程图的执行器链中的一个执行时机之一
applyPreHandle
,他会去执行所有拦截器链中的每个拦截的applyPreHandle()方法- 我们自定义的拦截器
MyInterceptor.preHandle
- 执行所有拦截器链中的每个拦截的
applyPreHandle()方法
- 每执行成功一个
this.interceptorIndex
就会给赋上i的值(拦截器变量的索引)
-
当某个拦截器中的
preHandle()
返回了false
,就会触发执行triggerAfterCompletion
-
triggerAfterCompletion
这里因为上面每次执行preHandle都会记录一下
拦截器变量的索引
所以如果有一个preHandle执行返回了false,那么这里就会
倒置的
去执行已经执行的拦截器的afterCompletion()
方法
8、handle
ha.handle(processedRequest, response, mappedHandler.getHandler());
这里是
真正执行
我们这次请求处理的controller对应的方法那阿昌这里就好奇了,他是如何执行,并拿到对应的结果的,这个结果封装在哪里???
- handleInternal()
- invokeHandlerMethod()
- invokeAndHandle()
- invokeForRequest
@Nullable public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception { //获取这次请求的参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } //执行对应的controller方法 return doInvoke(args); }
- doInvoke
这里就是controller的
代理
了,对应的代理设计模式- invoke
那上面拿到执行了controller的方法后,拿到的结果是如何处理的呢?
- handleReturnValue
- writeWithMessageConverters
将处理响应的结果,写入响应中,这个方法很长,对应与servletHttp响应的介绍
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; Class<?> valueType; Type targetType; if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } if (isResourceType(value, returnType)) { outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) { Resource resource = (Resource) value; try { List<HttpRange> httpRanges = inputMessage.getHeaders().getRange(); outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value()); body = HttpRange.toResourceRegions(httpRanges, resource); valueType = body.getClass(); targetType = RESOURCE_REGION_LIST_TYPE; } catch (IllegalArgumentException ex) { outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength()); outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); } } } MediaType selectedMediaType = null; MediaType contentType = outputMessage.getHeaders().getContentType(); boolean isContentTypePreset = contentType != null && contentType.isConcrete(); if (isContentTypePreset) { if (logger.isDebugEnabled()) { logger.debug("Found 'Content-Type:" + contentType + "' in response"); } selectedMediaType = contentType; } else { HttpServletRequest request = inputMessage.getServletRequest(); List<MediaType> acceptableTypes = getAcceptableMediaTypes(request); List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType); if (body != null && producibleTypes.isEmpty()) { throw new HttpMessageNotWritableException( "No converter found for return value of type: " + valueType); } List<MediaType> mediaTypesToUse = new ArrayList<>(); for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (mediaTypesToUse.isEmpty()) { if (body != null) { throw new HttpMediaTypeNotAcceptableException(producibleTypes); } if (logger.isDebugEnabled()) { logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes); } return; } MediaType.sortBySpecificityAndQuality(mediaTypesToUse); for (MediaType mediaType : mediaTypesToUse) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (logger.isDebugEnabled()) { logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes); } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> converter : this.messageConverters) { GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]"); addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } if (body != null) { Set<MediaType> producibleMediaTypes = (Set<MediaType>) inputMessage.getServletRequest() .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) { throw new HttpMessageNotWritableException( "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'"); } throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } }
重要的部分在:
body
:controller处理的结果
outputMessage
:上面封装的webRequest
中拿到的响应输出
//将通过converter将内容body写入outputMessage ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
- write()
t
:就是我们的结果“我是结果”@Override public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() { @Override public OutputStream getBody() { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } })); } else { //将t写入到outputMessage writeInternal(t, outputMessage); //flush刷新响应body outputMessage.getBody().flush(); } }
他是如何写入outputMessage的呢?
- writeInternal
将t写入到outputMessage
- copy
就直接通过
OutputStreamWriter
直接调用write
写入in也就是结果,最后flush
刷新执行完写入响应结果后,他就会
提交
这次响应,什么是 提交响应呢?介绍:响应提交
9、小总结
-
上面我们拿到对应的
HandlerExecutionChain拦截器链
这里包含:↓
- 处理器handlermapping
- 拦截器链
- 这次请求
-
从HandlerExecutionChain拦截器链到拿到对应的
handlerMapping
-
根据handlerMapping,去获取对应的
HandlerAdapter
的处理器对应的适配器
-
判断请求方式是否是
get
,如果是就判断是否被修改过 -
遍历所有的拦截器链,执行每个
applyPreHandle()
- 每次执行都记录执行的拦截器链遍历索引
- 如果有一个applyPreHandle返回false,就去导致执行triggerAfterCompletion,去执行
倒置
每一个已经执行applyPreHandle()方法的拦截器的afterCompletion()
方法
-
再通过适配器执行
真正处理
这次请求的controller方法handle
,返回ModelAndView
(这里我们不是jsp,所以肯定必然没有ModelAndView对应,就肯定为null)
10、applyPostHandle()
// Actually invoke the handler. //真正执行controller对应的方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //判断当前请求的所选处理程序是否选择处理异步处理 if (asyncManager.isConcurrentHandlingStarted()) { return; } //给对应返回的ModelAndView对象赋予默认名 applyDefaultViewName(processedRequest, mv); //倒置去执行每个拦截器链中每个拦截的applyPostHandle()方法 mappedHandler.applyPostHandle(processedRequest, response, mv);
倒置
去执行每个拦截器链中每个拦截的applyPostHandle()
方法
11、processDispatchResult()
在
processDispatchResult()
方法中,会做一些收尾的工作
这里会
判断是否需要去渲染视图ModelAndView
,因为我们这里是通过json响应返回,必然是没有modelAndView对象
去执行
triggerAfterCompletion()
,这里上面已经出现过,也就是去执行已经执行过preHandle
拦截器的afterCompletion()
方法这里会
倒置执行
已经执行过preHandle()方法的拦截器的afterCompletion()
方法
12、小总结
-
执行适配器对应的handle()方法也就是对应的controller的方法后,会返回
ModelAndView
-
判断当前请求的所选处理程序
是否选择处理异步处理
-
给对应返回的ModelAndView对象赋予
默认名
-
倒置
去执行每个拦截器链中每个拦截的applyPostHandle()
方法 -
执行
processDispatchResult()
做收尾工作- 去执行
triggerAfterCompletion()
,这里上面已经出现过,也就是去执行已经执行过preHandle
拦截器的afterCompletion()
方法
- 去执行
三、总结
-
执行这里就会遍历所有
this.handlerMappings
,获取请求的uri和请求,拿到对应能够处理这次请求的handlerMapping
,并将拿到:↓- handlerMapping处理器
- 对应的拦截器
- 这次的请求request
包装成一个
HandlerExecutionChain
。 -
从HandlerExecutionChain拦截器链到拿到对应的
handlerMapping
-
根据handlerMapping,去获取对应的
HandlerAdapter
的处理器对应的适配器
-
判断请求方式是否是
get
,如果是就判断是否被修改过 -
遍历所有的拦截器链,执行每个
applyPreHandle()
- 每次执行都记录执行的拦截器链遍历索引
- 如果有一个applyPreHandle返回false,就去导致执行triggerAfterCompletion,去执行
倒置
每一个已经执行applyPreHandle()方法的拦截器的afterCompletion()
方法
-
再通过适配器执行
真正处理
这次请求的controller方法handle
,返回ModelAndView
(这里我们不是jsp,所以肯定必然没有ModelAndView对应,就肯定为null)
-
执行适配器对应的handle()方法也就是对应的controller的方法后,会返回
ModelAndView
-
判断当前请求的所选处理程序
是否选择处理异步处理
-
给对应返回的ModelAndView对象赋予
默认名
-
倒置
去执行每个拦截器链中每个拦截的applyPostHandle()
方法 -
执行
processDispatchResult()
做收尾工作- 判断是否有
ModelAndView
,有就渲染,没有执行下面的 - 去执行
triggerAfterCompletion()
,这里上面已经出现过,也就是去执行已经执行过preHandle
拦截器的afterCompletion()
方法
- 判断是否有
四、结尾
以上SpringMVC的流程就执行完毕了,因为不涉及到jsp的流程,所有
DispatcherServlet
就拿到上面处理的响应结果,因为ModelAndView
为空,就没请求渲染视图了,直接响应tomcat,然后tomcat就返回给响应请求了以上就是这次的全部内容,感谢你能看到这里 (๑ˉ∀ˉ๑)!!!
-
SpringMVC执行流程图
2022-03-18 16:37:56 -
详解SpringMVC执行流程
2021-03-22 21:09:10SpringMVC 执行流程 SpringMVC 执行流程整体如下: 执行流程分析 (1)浏览器提交请求到中央调度器。 (2)中央调度器直接将请求转给处理器映射器。 (3)处理器映射器会根据请求,找到处理该请求的处理器,并将其... -
Springmvc执行流程介绍
2020-12-13 17:27:062.springmvc执行流程详细介绍 第一步:发起请求到前端控制器(DispatcherServlet) 第二步:前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找 第三步:处理器映射器HandlerMapping向前... -
图解springmvc 执行流程
2020-11-10 15:08:57HandleAdapter 处理适配器,将handle找到的方法执行,执行结果,即ModelAndView数据和视图返回给核心控制器。 HttpMessageConvertor 消息转换器,数据类型的转换,如日期… ViewResolver 视图解析器,核心控制器调度... -
springMVC执行流程
2018-09-17 21:35:261.请求发起 2.中央调度器,把请求(some.do)交给了处理器的映射器。 映射器:实现了HandlerMapping接口的类叫做处理器的映射器。 映射器作用:根据请求...3.中央调度器,把处理器执行链中的处理器对象(MyControl... -
springMVC 执行流程
2020-03-12 13:59:17具体可以查看项目启动函数,比如,run函数点进去就可以看到,或者通过启动日志可以查看 -
SpringMVC 执行流程(深入源码)
2020-08-12 23:55:50SpringMVC 执行流程1. 简介2. 深入源码2.1 Debug2.2 doDispatch2.2.1 getHandler(processedRequest)2.2.2 getHandlerAdapter(mappedHandler.getHandler())2.2.3 handle(HttpServletRequest request, ... -
【阅读SpringMVC源码】调试观察验证SpringMVC执行流程
2022-03-15 07:21:52【阅读SpringMVC源码】调试观察验证SpringMVC执行流程 -
springmvc执行流程-拦截器
2021-09-29 16:39:45springmvc的拦截器 springmvc可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口或者可以继承HandlerInterceptorAdapter(对HandlerInterceptor... -
SpringMVC执行流程面试这么说
2020-04-27 09:38:45SpringMVC是一个控制层的MVC框架,核心是一个Servlet,也就是和JSF一样面向方法级的MVC框架。...执行流程 用户发送一个请求到核心控制器DispatcherServlet,DispatcherServlet收到请求后会调用HandlerMapping...