SpringBoot 中DispatcherServlet请求分发流程源码分析

以一个简单的请求为例(实际RedisController包含多个请求)

@RestController
@RequestMapping("/redis")
public class RedisController {


    @RequestMapping(value = {"/getmsg"})
    public String getmsg(){

        return  "hello";
    }

    .....

}

 

整个 DispatcherServlet执行流程

正常情况下整个request流程代码都在DispatcherServlet#doService()方法中执行,由同一个线程完成从请求到响应

FrameworkServlet#doPost/doGet

FrameworkServlet#processRequest

DispatcherServlet#doService

DispatcherServlet#doDispatch

doDispatch是SpringMvc中的核心请求处理方法,几乎全部的请求处理流程都在这个方法中。下图就是doDispatch方法的主要执行流程。

SpringMVC工作原理详解

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);

				//根据request信息Uri找到对应HandlerExecutionChain执行链
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
                                        //没有找到HandlerExecutionChain 的通过response反馈
					noHandlerFound(processedRequest, response);
					return;
				}

				//通过已找到的Handler取得HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				//缓存
				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;
					}
				}
                                //pre拦截器执行
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//执行handle,Controller方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
                                //post拦截器执行(倒序)
				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);
				}
			}
		}
	}

DispatcherServlet#getHandler

可以看到springboot启动时装载的全部handlerMapping都保存在List<HandlerMapping> handlerMappings中。这里遍历handlerMappings中所有handlerMapping,直到找到请求对应的Mapping并返回执行立链HandlerExecutionChain。因为是@RequestMapping映射,所以遍历到handlerMappings[1]==RequestMappingHandlerMapping可以找到对应执行链。

下边直接步进到RequestMappingHandlerMapping其继承自AbstractHandlerMapping。

RequestMappingHandlerMapping初始化时注册@RequestMapping映射

https://blog.csdn.net/shanchahua123456/article/details/89816167

RequestMappingHandlerMapping成员变量

AbstractHandlerMapping#getHandler

AbstractHandlerMethodMapping#getHandlerInternal 

 AbstractHandlerMethodMapping其继承自AbstractHandlerMapping,实现了getHandlerInternal()。

这里取得mappingRegistry的读锁,后续就是通过URL从mappingRegistry匹配HandlerMethod(执行方法)。

mappingRegistry中注册了SpringMvc的控制器和HandlerMethod,以及URL的映射关系。下面有mappingRegistry的成员变量介绍。

AbstractHandlerMethodMapping#lookupHandlerMethod

AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl

到这里可以看到通过urlPath从urlLookUp中找到 

返回List<RequestMappingInfo>

AbstractHandlerMethodMapping#lookupHandlerMethod

AbstractHandlerMethodMapping#addMatchingMappings

RequestMappingInfoHandlerMapping#getMatchingMapping

AbstractHandlerMethodMapping.MappingRegistry#getMappings

AbstractHandlerMethodMapping#addMatchingMappings

到这里可以看到Spring已经通过mappingRegistry匹配到URL对应HandlerMethod对象,其成员变量包括: Bean对象,Method对象,参数列表等信息。将匹配到的HandlerMethod对象等信息保存到matches中。

mappingRegistry

mappingRegistry对象中成员变量:各种Map还有一个读写锁。

mappingRegistry的各个主要变量

 

 

返回 AbstractHandlerMethodMapping#lookupHandlerMethod 

返回lookupHandlerMethod()中从返回的List<Match> matches中匹配最合适的handlerMethod返回getHandlerInternal()方法。

if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					String uri = request.getRequestURI();
					throw new IllegalStateException(
							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}

返回 AbstractHandlerMethodMapping#getHandlerInternal 

此时已取得 handlerMethod。 

 

返回 AbstractHandlerMapping#getHandler

此时已取得 handlerMethod,剩下的工作是完成执行链HandlerExecutionChain

取得执行链HandlerExecutionChain

AbstractHandlerMapping#getHandlerExecutionChain 

创建HandlerExecutionChain(handler),遍历ArrayList<HandlerInterceptor> adaptedInterceptors把interceptor加入其中。

返回AbstractHandlerMapping#getHandler

返回DispatcherServlet#getHandler

此时已取得完整执行链,回到最初的DispatcherServlet类中。

可以看到IDE提示HandlerExecutionChain handler中包含HandlerMethod和两个拦截器 

返回DispatcherServlet#doDispatch

取得执行链HandlerExecutionChain 后,取得HandlerAdapter。

HandlerExecutionChain#getHandler

上图mappedHandler为之前取得的执行链HandlerExecutionChain ,取得执行链中的handler。

DispatcherServlet#getHandlerAdapter 

遍历handlerAdapters找到合适。

 返回DispatcherServlet#doDispatch

此时回到doDispatch方法已经获得HandlerExecutionChain和HandlerAdapter。剩下的流程执行前置拦截器,执行Controller,执行post拦截器。

HandlerExecutionChain mappedHandler执行pre拦截器

HandlerExecutionChain#applyPreHandle

执行PRE拦截器,此处将当前执行链中的(HandlerMethod)this.handler传入拦截器。

返回DispatcherServlet#doDispatch

继续执行Controller方法(Handler)

AbstractHandlerMethodAdapter#handle

RequestMappingHandlerAdapter#handleInternal 

RequestMappingHandlerAdapter#invokeHandlerMethod

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
                //webRequest : uri=/redis/getmsg;client=127.0.0.1
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

最终会走到此方法中InvocableHandlerMethod#doInvoke

通过HandlerMethod中的BridgedMethod反射执行Controller中的@RequestMapping映射方法调用。

getBridgedMethod().invoke(getBean(), args);

getBridgedMethod()返回的是HandlerMethod中的bridgedMethod(下图)。

getBean()返回的是HandlerMethod中的bean(下图)。 

RedisController#getmsg 

 此时通过放射调用,执行真正的RedisController中的@RequestMapping映射方法

一直返回到ServletInvocableHandlerMethod#invokeAndHandle

ModelAndViewContainer mavContainer: View is [null]; default model {}  因为是@RestController,View=null

returnValue="hello"

RequestMappingHandlerAdapter#invokeHandlerMethod

通过getModelAndView()将mavContainer转换成ModelAndView返回

 

一直返回DispatcherServlet#doDispatch

Controller@RequestMapping映射方法执行完毕,返回MV。向下执行POST拦截器。

HandlerExecutionChain#applyPostHandle

倒序执行Post拦截器(后进先出)

响应用户请求DispatcherServlet#processDispatchResult

 

 

版权声明:本文为shanchahua123456原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/shanchahua123456/article/details/87541241

智能推荐

java反编译

jvm 把Boolean类型的值flag当做int类型处理。​​​ Foo.java: 由 class 文件生成 jasm 文件:java -jar asmtools.jar jdis Foo.class > Foo.jasm  修改jasm文件: 执行反编译: java -jar jd-gui-1.6.6.jar File 打开Foo.class文件:b修改为2 重新执行java...

【学习笔记】03-v-html的学习和示例

v-html的认识和使用 示例: 显示结果: 注意:v-html是有复制的...

Java实现在线考试系统(系统介绍)

1.和现在有的考试系统有以下几种优势: a.和现在有的系统比较起来,本系统有科目、章节、老师、学生、班级等信息的管理,还有批阅试卷查看已批阅试卷等。传统的考试系统划分并不细,业务功能简单。 b.和学校的考试系统还有外面的考试系统比较起来,本系统是B/S结构,学校的考试系统一般为C/S结构,性能方面不如B/S结构,并且C/S接口需要安装客户端,客户端压力很大,我的系统只需要电脑具有浏览器,在同一局域...

计算机视觉--多视几何初步尝试

基础矩阵的原理 K和K’分别是两个相机的参数矩阵。p和p’是X在平面π的坐标表示。所以可以得出 具体计算过程 代码: #!/usr/bin/env python coding: utf-8 from PIL import Image from numpy import * from pylab import * import numpy as np from imp ...

java初学者怎么学习才可以快速入门

java初学者怎么学习才可以快速入门 一、了解JAVA 我们要知道:Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言。 Java之父:詹姆斯·高斯林 1.1 java的三个体系 Java SE(Java Platform Standard Edition)。Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境...

猜你喜欢

字段属性之主键&增删改查&自增长&唯一键约束

字段属性之主键&自增长&唯一键约束 主键 主键:primary key 主要的键 一张表中只有一个字段可以使用对应的键,用来唯一的约束该字段里面的数据,不能重复,这种称之为主键 一张表只能最多一个主键 增加主键 SQL操作中有多种方式增加主键大体分为三种 1.在创建表的时候直接在字段之后跟primary key关键字(主键本身不允许为空) 优点:非常直接:缺点:只能使用一个字段作为...

linux下 基于libmad的socket多用户mp3音频在线播放服务器

在众多大神的帮助下,这个在线播放流媒体服务器终于完成啦。。。。 这个mp3流媒体服务器设计的思路是,服务器程序server用多线程实现和多个客户端的通信(这是必然的),然后发送给客户端当前的音频列表公客户端选择,之后根据k客户端的选择给多个客户端传输相应mp3文件的数据,同时,客户端进行实时地音频解码并播放。 关于libmad开源mp3音频解码库的使用,见上一篇博客吧。。。。 在服务器程序这一端,...

Nginx

Nginx Nginx简介: Nginx是一个高性能的http和反向代理服务器,特点是有内存少,并发能力强,事实上Nginx的并发能力确实在同类型网页服务器中表现较好, Nginx用作web服务器:Nginx可以作为静态页面的web服务器,同时还支持CGI语言,但不支持java,java程序只能通过Tomcat配合完成。Nginx专为性能优化而开发,性能是其最重要的考量,实现上非常注重效率,能经受...

SpringCloud Alibaba - Sentinel入门案例(二)(流控规则 | 直接 / 关联 / 链路 / 快速失败 / Warm Up / 排队等待)

SpringCloud Alibaba - Sentinel入门案例(二)(流控规则 | 直接 / 关联 / 链路 / 快速失败 / Warm Up / 排队等待) 回溯 Sentinel 基本概念 正文 环境准备 流控规则介绍 简单介绍 对 阈值类型 / 单机阈值做 测试 流控模式 直接流控模式 关联流控模式 链路流控模式 资源名称的修改 链路模式正文 坑来了,怎么解决? 禁止收敛URL的入口 ...

SQL Server 无法创建索引 因为对象名称和索引名称重复

需求分析:如下图所示,给T_DD_OP1200_Final表的Id字段添加主键,保存时弹出 “T_DD_OP1200_Final”表 - 无法创建索引“PK_T_DD_OP1200_Final”。 因为发现对象名称 ‘dbo.T_DD_OP1200_Final’ 和索引名称 ‘PK_T_DD_OP1200_Fina...