飞行的蜗牛

vuePress-theme-reco 极客学长    2013 - 2025
飞行的蜗牛 飞行的蜗牛

Choose mode

  • dark
  • auto
  • light
首页
分类
  • 技术杂谈
  • Database
  • Docker
  • PHP
  • 随笔杂谈
  • 前端开发
  • FunnyTools
  • Jekyll
  • 读书笔记
  • Java
  • SpringBoot
  • 区块链技术
  • IPFS
  • C/C++
  • Filecoin
  • Golang
  • Sharding-JDBC
  • 分布式存储
  • Lotus-源码系列
  • Lotus
  • 框架源码系列
  • Spring-源码系列
  • AI
  • ChatGPT
  • Stable Diffusion
  • DeepSeek-R1
  • DeepSeek-V3
标签
时间抽
关于作者
开源项目
GeekAI (opens new window)
author-avatar

极客学长

154

文章

151

标签

首页
分类
  • 技术杂谈
  • Database
  • Docker
  • PHP
  • 随笔杂谈
  • 前端开发
  • FunnyTools
  • Jekyll
  • 读书笔记
  • Java
  • SpringBoot
  • 区块链技术
  • IPFS
  • C/C++
  • Filecoin
  • Golang
  • Sharding-JDBC
  • 分布式存储
  • Lotus-源码系列
  • Lotus
  • 框架源码系列
  • Spring-源码系列
  • AI
  • ChatGPT
  • Stable Diffusion
  • DeepSeek-R1
  • DeepSeek-V3
标签
时间抽
关于作者
开源项目
GeekAI (opens new window)
  • Spring MVC源码学习笔记

    • 01 - Spring MVC 主要流程
      • 02 - 初始化 DispatcherServlet
        • 03 - 查找 HandlerMapping 流程
          • 04 - 控制器 RequestMap 匹配流程
            • 05 - 匹配 HandlerAdapter 流程完成
              • 06 - 渲染视图流程
                • 07 - 手写简易实现

                Spring MVC源码学习笔记

                vuePress-theme-reco 极客学长    2013 - 2025

                Spring MVC源码学习笔记


                极客学长 2022-11-18 0 Spring 源码系列

                万维钢-《才华与野心》

                学问能让你 [配得上] 财富,但是并不一定能带来财富。

                但如果一个人搞不清自己到底想要什么,一边说着要求知一边又羡慕人家的成就,自己又没有太大野心去狠心做事,那就是人生观不够自洽。

                # 01 - Spring MVC 主要流程

                先看一张 Spring MVC 请求响应的具体流程图:

                1. 客户端发送请求到前端控制器 DispatcherServlet
                2. DispatcherServlet 找到合适的处理器(HandleMapping) ,并生成处理器执行链(HandlerExecutionChain)返回给 DispatcherServlet。
                3. 调用拦截器的 preHandle() 方法,如果返回 false, 则直接返回,不在进行后面的处理。
                4. DispatcherServlet 根据 HandlerMapping 找到处理器适配器(HandlerAdapter)。
                5. 执行 HandlerAdapter.handle()方法,返回一个 ModelAndView 对象。
                6. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器, 返回一个视图对象(View)。
                7. 调用拦截器的 postHandle() 方法。
                8. DispatcherServlet 调用模板引擎工具(JSP,Freemarker,Thymeleaf 等)对 View 进行渲染视图(即将模型数据 model 填充至视图中)。
                9. 调用拦截器的 afterCompletion() 方法。
                10. DispatcherServlet 将渲染结果返回给客户端。

                SpringMVC 的请求入口在 FrameworkServlet::processRequest(request, response) 方法, 调用 DispathcherServlet::doService(request, response) 方法来初始化 DispatcherServlet。

                主要执行过程实现在 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);
                
                				// 进行映射
                				mappedHandler = getHandler(processedRequest);
                				if (mappedHandler == null) {
                					noHandlerFound(processedRequest, response);
                					return;
                				}
                
                				// 适配 HandlerMapper, 找到最合适的 HandlerAdapter
                				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                
                				// Process last-modified header, if supported by the handler.  HTTP缓存相关
                				String method = request.getMethod();
                				boolean isGet = HttpMethod.GET.matches(method);
                				if (isGet || HttpMethod.HEAD.matches(method)) {
                					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                						return;
                					}
                				}
                				// 触发前置拦截器:preHandle()
                				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                					// 返回false就不进行后续处理了
                					return;
                				}
                
                				// Actually invoke the handler.
                				// 调用对应的控制器方法(handleRequest(request, response)),返回一个 ModelAndView 对象
                				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                
                				if (asyncManager.isConcurrentHandlingStarted()) {
                					return;
                				}
                				// 如果mv有  视图没有,给你设置默认视图
                				applyDefaultViewName(processedRequest, mv);
                				//触发后置拦截器:postHandle()
                				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);
                			}
                			// 渲染视图,并触发拦截器:afterCompletion()
                			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);
                				}
                			}
                		}
                	}
                

                逻辑非常清晰,看完这个方法基本上就对 SpringMVC 实现流程有了大体的了解。然后再进入对应的核心方法内部去看具体细节流程。

                # 02 - 初始化 DispatcherServlet

                DispatcherServlet 的初始化工作是在 DispatcherServlet.initStrategies() 方法中进行的:

                protected void initStrategies(ApplicationContext context) {
                	// 初始化文件上传解析器
                	initMultipartResolver(context);
                	initLocaleResolver(context);
                	initThemeResolver(context);
                	// 初始化 HandlerMapping
                	initHandlerMappings(context);
                	// 初始化 HandlerAdapter
                	initHandlerAdapters(context);
                	initHandlerExceptionResolvers(context);
                	initRequestToViewNameTranslator(context);
                	// 初始化视图解析器
                	initViewResolvers(context);
                	initFlashMapManager(context);
                }
                

                各种资源的初始化的一般逻辑都是先到 Spring Bean 容器中去找,找不到的话就使用 DispatcherServlet.properties 文件中配置的默认资源,以 HandlerMapping 为例,它具体的初始化逻辑如下:

                1. 去当前 Bean 工厂以及父 Bean 工厂中去查找 HandlerMapping 类别的 Bean,找到了就直接排序后赋值给 handlerMappings。

                  Map<String, HandlerMapping> matchingBeans =
                  		BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                  if (!matchingBeans.isEmpty()) {
                  	this.handlerMappings = new ArrayList<>(matchingBeans.values());
                  	// We keep HandlerMappings in sorted order.
                  	AnnotationAwareOrderComparator.sort(this.handlerMappings);
                  }
                  

                  这里传入的 includeNonSingletons=true, allowEagerInit=false,表明结果中包含非单例 Bean,但是不包含半成品 Bean

                2. 如果根据类型没有找到,则通过 context.getBean() 方法获取 beanName 为 handlerMapping 类型 为 HandlerMapping 的 Bean:

                  HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                  this.handlerMappings = Collections.singletonList(hm);
                  
                3. 如果还是没有找到 , 就去拿默认的, 默认有三个:BeanNameUrlHandlerMapping, RequestMappingHandlerMapping, RouterFunctionMapping:

                  if (this.handlerMappings == null) {
                  	this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                  }
                  

                DispatcherServlet.properties 文件内容:

                # Default implementation classes for DispatcherServlet's strategy interfaces.
                # Used as fallback when no matching beans are found in the DispatcherServlet context.
                # Not meant to be customized by application developers.
                
                org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
                
                org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
                
                org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
                   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
                   org.springframework.web.servlet.function.support.RouterFunctionMapping
                
                org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
                   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
                   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
                   org.springframework.web.servlet.function.support.HandlerFunctionAdapter
                
                
                org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
                   org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
                   org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
                
                org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
                
                org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
                
                org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
                

                # 03 - 查找 HandlerMapping 流程

                DispatcherServlet 查找 HandlerMapping 的流程很简单,就是遍历所有的 HandlerMapping,然后一个一个匹配 Request,匹配上了就立刻返回,不在继续匹配,意味着谁解析到就用谁。

                protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                	if (this.handlerMappings != null) {
                		for (HandlerMapping mapping : this.handlerMappings) {
                			HandlerExecutionChain handler = mapping.getHandler(request);
                			// 匹配到一个立即返回,不再继续匹配
                			if (handler != null) {
                				return handler;
                			}
                		}
                	}
                	return null;
                }
                

                进一步追踪 HandlerMapping.getHandler() 方法,这个方法具体实现是在抽象类 AbstractHandlerMapping 中,主要逻辑代码如下(去掉了一些不重要的代码),注意看注释:

                public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                    // K2: 核心方法,通过请求 URI 来解析到请求对应的 Handler Method
                	Object handler = getHandlerInternal(request);
                	// 如果没匹配到,则获取默认的 Handler,也就是那个匹配 "/*" 的 Handler
                	if (handler == null) {
                		handler = getDefaultHandler();
                	}
                	if (handler == null) {
                		return null;
                	}
                    ...
                    // 生成一个控制器执行链
                	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
                    ...
                	return executionChain;
                }
                

                在生成控制器执行链的时候,会把当前请求匹配到的所有拦截器加入到执行链。

                protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
                	HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
                	// 筛选出所有跟当前请求匹配的拦截器
                	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
                		if (interceptor instanceof MappedInterceptor) {
                			MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                			// 如果匹配上了,就把拦截器加入执行链
                			if (mappedInterceptor.matches(request)) {
                				chain.addInterceptor(mappedInterceptor.getInterceptor());
                			}
                		}
                		else {
                			chain.addInterceptor(interceptor);
                		}
                	}
                	return chain;
                }
                

                # 04 - 控制器 RequestMap 匹配流程

                这是一个非常核心的流程,即如何通过请求 Path 和请求 METHOD 找到对应的控制器和方法。

                1. 通过 path 作为 Key 直接去 pathLookup Map 中查找,找到了就直接使用。

                2. 如果无 path 匹配,用所有的 RequestMappingInfo,通过 AntPathMatcher 匹配,也就是解析表达式。

                3. 如果匹配到多个,则对所有匹配结果进行优先级排序,排序后选择第一个作为最终匹配结果。

                  示例

                  假如请求 path 是 /test,Matcher 匹配到 4 个结果:

                  • @RequestMapping(value="/test?")
                  • @RequestMapping(value="/test*")
                  • @RequestMapping(value="/{xxxx}")
                  • @RequestMapping(value="/**")

                  则优先级大致就是 ? > * > {} >**

                具体代码实现在 AbstractHandlerMethodMapping.lookupHandlerMethod() 方法中,有兴趣去看看详细流程,这里贴出删减版代码:

                protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
                	List<Match> matches = new ArrayList<>();
                	List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
                	if (directPathMatches != null) {
                		// 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
                		addMatchingMappings(directPathMatches, matches, request);
                	}
                	if (matches.isEmpty()) {
                		// 如果无path匹配,用所有的RequestMappingInfo  通过AntPathMatcher匹配
                		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
                	}
                	if (!matches.isEmpty()) {
                		// 选择第一个为最匹配的
                		Match bestMatch = matches.get(0);
                		if (matches.size() > 1) {
                			//创建MatchComparator的匹配器对象
                			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                			matches.sort(comparator);
                
                			// 排完序后拿到优先级最高的
                			bestMatch = matches.get(0);
                		}
                		//把最匹配的设置到request中
                		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
                		handleMatch(bestMatch.mapping, lookupPath, request);
                		//返回最匹配的
                		return bestMatch.getHandlerMethod();
                	}
                	else { // return null
                		return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
                	}
                }
                

                # 05 - 匹配 HandlerAdapter 流程完成

                匹配 HandlerAdapter 的方法也很简单,就是遍历所有的 HandlerAdapter, 用当前的找到 Handler 去匹配,匹配成功就返回。

                HandlerAdapter 主要有三种,分别对应三种控制器:

                1. HttpRequestHandlerAdapter: 对应的 Handler 要实现 HttpRequestHandler 接口。
                2. SimpleControllerHandlerAdapter: 对应的 Handler 要实现 Controller 接口。
                3. RequestMappingHandlerAdapter: 对应通过解析 @RequestMap 注解得到的 Handler。

                另外控制器方法的调用流程在 RequestMappingHandlerAdapter.invokeHandlerMethod() 方法中。

                # 06 - 渲染视图流程

                1. 解析视图名称,把视图名称拼上在视图解析器中配置好的前缀,后缀,得到模板文件的完整路径。
                2. 通过 ModelAndView 创建视图对象。
                3. 调用 View.render(model, request, response) 方法,填充属性,渲染视图。

                详细源码实现在 DispatcherServlet.render() 方法中。

                # 07 - 手写简易实现

                搞懂 Spring MVC 的原理之后,我从零手写实现了一个简易版本的 Spring MVC 框架,模拟跑了一下整个流程。写完之后对整个流程又有了更清楚的理解,很多东西看着别人的源码实现觉得很简单,但是真到自己上手写的时候,才发现脑子知道和手知道完全是两码事。

                源码地址:https://gitee.com/blackfox/spring-samples/tree/master/spring-mvc-custom-impl (opens new window)

                本站博文如非注明转载则均属作者原创文章,引用或转载无需申请版权或者注明出处,如需联系作者请加微信: geekmaster01

                Spring 事务源码学习笔记 Spring 学习总结