基本概念 DispatcherServlet 是 Spring MVC 框架的核心组件,作为一个 前端控制器 (Front Controller)负责将所有传入的 HTTP 请求分发给适当的处理器(Controller)并协调视图解析和响应生成。它的作用是集中管理 Web 应用中的所有请求调度流程,使得应用能够解耦并保持整洁有序的架构。
作用 前端控制器模式的实现 :
DispatcherServlet
实现了前端控制器模式(Front Controller)即所有客户端请求首先都会经过该Servlet,然后由它进行统一处理。
通过这种集中管理的方式,可以避免每个控制器直接处理请求,从而提高了系统的可维护性和扩展性。
请求分发 :
DispatcherServlet
根据 URL 和请求参数,使用 HandlerMapping 找到与请求对应的处理器(通常是控制器类中的某个方法)。
它不仅负责路由请求,还能够处理请求参数的解析、验证等操作。
调用处理器 :
找到合适的处理器后,DispatcherServlet
使用 HandlerAdapter 负责调用处理器(Controller)并将请求数据传递给它。
HandlerAdapter
是将请求与控制器方法参数适配的组件,可以处理多种类型的控制器,包括注解式控制器、简单的控制器等。
视图解析 :
处理器返回视图名称和模型数据后,DispatcherServlet
使用 ViewResolver 解析视图(例如 JSP、Thymeleaf 等模板引擎)找到对应的视图模板。
它负责将模型数据注入到视图模板中,最终渲染成 HTML 响应。
异常处理与响应生成 :
DispatcherServlet
还负责处理异常,将错误信息封装为用户友好的响应。
DispatcherServlet
将生成的 HTML 或其他类型的响应数据写入 HttpServletResponse
,返回给客户端。
流程图
首先用户发送请求——>DispatcherServlet ,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控制
DispatcherServlet——>HandlerMapping , HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一 个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新 的映射策略
DispatcherServlet——>HandlerAdapter ,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器
HandlerAdapter——>处理器功能处理方法的调用 ,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处 理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名)
ModelAndView 的逻辑视图名——> ViewResolver ,ViewResolver 将把逻辑视图名解析为具体的View,通过这种策 略模式,很容易更换其他视图技术
View——>渲染 ,View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此 很容易支持其他视图技术
返回控制权给DispatcherServlet ,由DispatcherServlet 返回响应给用户,到此一个流程结束
调试分析 控制器
@RequestMapping("/thymeleaf") public String thymeleafExample (@RequestParam("code") String code, Model model) { model.addAttribute("code" , code); return "thymeleaf/index" ; }
视图内容
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" > <head> <meta charset="UTF-8" > <title>Thymeleaf Example</title> </head> <body> <h1>Thymeleaf Example</h1> <p>Code: <span th:text="${code}"></span></p> </body> </html>
doGet入口 servlet处理get请求是doGet方法,所以定位到 DispatcherServlet 的 doGet 方法
protected final void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this .processRequest(request, response); }
processRequest方法如下
protected final void processRequest (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null ; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = this .buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = this .buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); this .initContextHolders(request, localeContext, requestAttributes); try { this .doService(request, response); } catch (IOException | ServletException var16) { failureCause = var16; throw var16; } catch (Throwable var17) { failureCause = var17; throw new NestedServletException("Request processing failed" , var17); } finally { this .resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null ) { requestAttributes.requestCompleted(); } this .logResult(request, response, (Throwable)failureCause, asyncManager); this .publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause); } }
核心调用doService方法
protected void doService (HttpServletRequest request, HttpServletResponse response) throws Exception { this .logRequest(request); Map<String, Object> attributesSnapshot = null ; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap(); Enumeration<?> attrNames = request.getAttributeNames(); label116: while (true ) { String attrName; do { if (!attrNames.hasMoreElements()) { break label116; } attrName = (String)attrNames.nextElement(); } while (!this .cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet" )); attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this .getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this .localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this .themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, this .getThemeSource()); if (this .flashMapManager != null ) { FlashMap inputFlashMap = this .flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null ) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this .flashMapManager); } RequestPath previousRequestPath = null ; if (this .parseRequestPath) { previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE); ServletRequestPathUtils.parseAndCache(request); } try { this .doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null ) { this .restoreAttributesAfterInclude(request, attributesSnapshot); } if (this .parseRequestPath) { ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request); } } }
请求分发 doDispatch 方法是真正处理请求的核心方法,重点函数已添加注释,该方法中主要的函数总结为:
getHandler:获取一个chain,包含:匹配出request中的处理器(即编写的controller) + 对应的拦截器
getHandlerAdapter:获取能执行chain的 处理器适配器
applyPreHandle:调用前置拦截器
ha.handle:调用处理器(controller)
applyDefaultViewName:获取视图名
applyPostHandle:调用后置拦截器
processDispatchResult:视图渲染处理
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null ; boolean multipartRequestParsed = false ; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = this .checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this .getHandler(processedRequest); if (mappedHandler == null ) { this .noHandlerFound(processedRequest, response); return ; } HandlerAdapter ha = this .getHandlerAdapter(mappedHandler.getHandler()); 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 ; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return ; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return ; } this .applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed" , var21); } this .processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this .triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this .triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed" , var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null ) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this .cleanupMultipart(processedRequest); } } }
映射器和适配器的处理 getHandler 遍历 handlerMappings
protected HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { if (this .handlerMappings != null ) { Iterator var2 = this .handlerMappings.iterator(); while (var2.hasNext()) { HandlerMapping mapping = (HandlerMapping)var2.next(); HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null ) { return handler; } } } return null ; }
这个handlerMappings:处理器映射,保存了每一个处理器可以处理哪些请求的方法的映射信息
RequestMappingHandlerMapping:基于注解,如@RequestMapping的请求映射
WelcomePageHandlerMapping:欢迎页面
BeanNameUrlHandlerMapping:根据 Bean 的名称来处理 URL 请求映射
RouterFunctionMapping:用于处理基于函数式编程风格的请求映射,而不是基于注解的方式
SimpleUrlHandlerMapping:简单的URL到控制器的直接映射,通常通过配置文件如application.properties
其中的 RequestMappingHandlerMapping 在内存马的学习中已经见到过,其保存的就是关于请求的信息
那么 RequestMappingHandlerMapping['mappingRegistry']['registry']
存储的就是对应的 @Controller 信息
跟进到核心方法:AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { Object handler = this .getHandlerInternal(request); if (handler == null ) { handler = this .getDefaultHandler(); } if (handler == null ) { return null ; } else { if (handler instanceof String) { String handlerName = (String)handler; handler = this .obtainApplicationContext().getBean(handlerName); } if (!ServletRequestPathUtils.hasCachedPath(request)) { this .initLookupPath(request); } HandlerExecutionChain executionChain = this .getHandlerExecutionChain(handler, request); if (this .logger.isTraceEnabled()) { this .logger.trace("Mapped to " + handler); } else if (this .logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) { this .logger.debug("Mapped to " + executionChain.getHandler()); } if (this .hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = this .getCorsConfiguration(handler, request); if (this .getCorsConfigurationSource() != null ) { CorsConfiguration globalConfig = this .getCorsConfigurationSource().getCorsConfiguration(request); config = globalConfig != null ? globalConfig.combine(config) : config; } if (config != null ) { config.validateAllowCredentials(); } executionChain = this .getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } }
一、跟进 AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal (HttpServletRequest request) throws Exception { String lookupPath = this .initLookupPath(request); this .mappingRegistry.acquireReadLock(); HandlerMethod var4; try { HandlerMethod handlerMethod = this .lookupHandlerMethod(lookupPath, request); var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null ; } finally { this .mappingRegistry.releaseReadLock(); } return var4; }
根据请求路径获取对应的处理方法(即自己写的controller对应的函数)
二、跟进AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain (Object handler, HttpServletRequest request) { HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler); Iterator var4 = this .adaptedInterceptors.iterator(); while (var4.hasNext()) { HandlerInterceptor interceptor = (HandlerInterceptor)var4.next(); if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor; if (mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
将handler作为初始参数,创建HandlerExecutionChain对象,然后遍历 adaptedInterceptors 集合,该集合里存放的都是拦截器,如果拦截器的类型是 MappedInterceptor,则调用 matches 方法去匹配一下,看一下是否是拦截当前请求的拦截器,如果是,则调用 chain.addInterceptor 方法加入到 HandlerExecutionChain 对象中;如果就是一个普通拦截器,则直接加入到 HandlerExecutionChain 对象中
通过一、二成功获取到chain对象
getHandlerAdapter 遍历handlerAdapters,匹配出能够支持处理方法的 处理器适配器
protected HandlerAdapter getHandlerAdapter (Object handler) throws ServletException { if (this .handlerAdapters != null ) { Iterator var2 = this .handlerAdapters.iterator(); while (var2.hasNext()) { HandlerAdapter adapter = (HandlerAdapter)var2.next(); if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler" ); }
这个handlerAdapters:HandlerAdapter的主要作用是根据处理器的类型调用相应的处理器方法。由于处理器的类型可能多种多样,如方法、类、Bean等,因此需要一个适配器来统一处理这些不同类型的处理器。HandlerAdapter就充当了这样的角色,它屏蔽了处理器的具体实现细节,使得DispatcherServlet可以以统一的方式调用处理器。
RequestMappingHandlerAdapter:用于适配基于注解的处理器方法,会解析注解中的信息,并调用相应的处理器方法
HandlerFunctionAdapter:用于适配基于函数式编程模型的处理器
HttpRequestHandlerAdapter:用于适配实现了HttpRequestHandler接口的处理器
SimpleControllerHandlerAdapter:用于适配实现了Controller接口的处理器,已被@Controller注解所取代
这里获取的就是 RequestMappingHandlerAdapter
applyPreHandle boolean applyPreHandle (HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0 ; i < this .interceptorList.size(); this .interceptorIndex = i++) { HandlerInterceptor interceptor = (HandlerInterceptor)this .interceptorList.get(i); if (!interceptor.preHandle(request, response, this .handler)) { this .triggerAfterCompletion(request, response, (Exception)null ); return false ; } } return true ; }
遍历调用 interceptorList 中的拦截器 preHandle 方法
ha.handle
https://blog.csdn.net/weixin_45480785/article/details/116595206
https://www.cnblogs.com/RunningSnails/p/17128123.html
handle -> handleInternal -> invokeHandlerMethod
public final ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return this .handleInternal(request, response, (HandlerMethod)handler); } 跟进 protected ModelAndView handleInternal (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { this .checkRequest(request); ModelAndView mav; if (this .synchronizeOnSession) { HttpSession session = request.getSession(false ); } else { mav = this .invokeHandlerMethod(request, response, handlerMethod); } } else { mav = this .invokeHandlerMethod(request, response, handlerMethod); } ...... return mav; }
核心函数 invokeHandlerMethod
protected ModelAndView invokeHandlerMethod (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); 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(); } }
最主要是为以下几步
绑定参数
创建ModelAndViewContainer对象
处理器调用
从ModelAndViewContainer中取出ModelAndView对象
有错别字的流程图如下
ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle (ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null ) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true ); return ; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true ); return ; } mavContainer.setRequestHandled(false ); Assert.state(this .returnValueHandlers != null , "No return value handlers" ); try { this .returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
一、跟进 InvocableHandlerMethod#invokeForRequest
public Object invokeForRequest (NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = this .getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } return this .doInvoke(args); }
二、跟进 InvocableHandlerMethod#doInvoke
反射调用handler方法
protected Object doInvoke (Object... args) throws Exception { Method method = this .getBridgedMethod(); try { return KotlinDetector.isSuspendingFunction(method) ? CoroutinesUtils.invokeSuspendingFunction(method, this .getBean(), args) : method.invoke(this .getBean(), args); }...... }
在 this.returnValueHandlers.handleReturnValue
处理返回值时,设置controller的return值作为view属性
if (returnValue instanceof CharSequence) { String viewName = returnValue.toString(); mavContainer.setViewName(viewName); if (isRedirectViewName(viewName)) { mavContainer.setRedirectModelScenario(true ); } }
最后通过 getModelAndView 获取 ModelAndView 对象
private ModelAndView getModelAndView (ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null ; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null ) { RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav; }
最后来观察一下ModelAndView对象,主要有两个主要属性
view:返回的视图文件名称,该例为 thymeleaf/index
model:是一个map,存储着在controller中通过addAttribute添加的属性,该例为 code => test123
applyDefaultViewName 判断ModelAndView中有没有view属性,换句话就是看你的controller代码中有没有return值,如果没有的话,将调用getDefaultViewName函数,将其返回值作为view属性。确保view属性不为空
private void applyDefaultViewName (HttpServletRequest request, @Nullable ModelAndView mv) throws Exception { if (mv != null && !mv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null ) { mv.setViewName(defaultViewName); } } }
跟进 DefaultRequestToViewNameTranslator#getViewName
public String getViewName (HttpServletRequest request) { String path = ServletRequestPathUtils.getCachedPathValue(request); return (this .prefix + transformPath(path) + this .suffix); }
会将URI path作为视图名称
applyPostHandle 再次执行拦截器
void applyPostHandle (HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { for (int i = this .interceptorList.size() - 1 ; i >= 0 ; i--) { HandlerInterceptor interceptor = this .interceptorList.get(i); interceptor.postHandle(request, response, this .handler, mv); } }
视图解析渲染 processDispatchResult private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered" , exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null ); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null ); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned." ); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return ; } if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, null ); } }
render 跟进核心函数:DispatcherServlet#render
protected void render (ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = (this .localeResolver != null ? this .localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null ) { view = resolveViewName(viewName, mv.getModelInternal(), locale, request); ... } else { view = mv.getView(); ... } ... try { ... view.render(mv.getModelInternal(), request, response); } ... }
涉及两个核心函数
resolveViewName:获取最匹配的视图解析器
view#render:调用视图解析器去渲染
resolveViewName 一、跟进
protected View resolveViewName (String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { if (this .viewResolvers != null ) { for (ViewResolver viewResolver : this .viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null ) { return view; } } } return null ; }
遍历视图解析器,分别调用各个视图解析器的resolveViewName方法根据viewName获取View对象(freemarker是自己多加的)
在众多视图解析器中,首先进来的是 ContentNegotiatingViewResolver#resolveViewName
public View resolveViewName (String viewName, Locale locale) throws Exception { ... if (requestedMediaTypes != null ) { List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); View bestView = getBestView(candidateViews, requestedMediaTypes, attrs); if (bestView != null ) { return bestView; } } ... }
又是两个核心函数
1、首先通过 getCandidateViews 筛选出 resolveViewName 方法返回值不为null的视图解析器添加到 candidateViews中
最后返回两个
2、之后通过 getBestView 拿到最适配的解析器,getBestView中的逻辑是优先返回在 candidateViews 存在重定向动作的 view,接着判断ContentType是否匹配返回最适配的 View
private View getBestView (List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) { for (View candidateView : candidateViews) { if (candidateView instanceof SmartView) { SmartView smartView = (SmartView) candidateView; if (smartView.isRedirectView()) { return candidateView; } } } for (MediaType mediaType : requestedMediaTypes) { for (View candidateView : candidateViews) { if (StringUtils.hasText(candidateView.getContentType())) { MediaType candidateContentType = MediaType.parseMediaType(candidateView.getContentType()); if (mediaType.isCompatibleWith(candidateContentType)) { mediaType = mediaType.removeQualityValue(); if (logger.isDebugEnabled()) { logger.debug("Selected '" + mediaType + "' given " + requestedMediaTypes); } attrs.setAttribute(View.SELECTED_CONTENT_TYPE, mediaType, RequestAttributes.SCOPE_REQUEST); return candidateView; } } } } return null ; }
最后返回ThymeleafView的view对象
render 目前已经获取了view对象(ThymeleafView)
进入其render函数,调用 renderFragment
public void render (Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { this .renderFragment(this .markupSelectors, model, request, response); }
renderFragment中首先定义了一大堆的上下文
protected void renderFragment (Set<String> markupSelectorsToRender, Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { ServletContext servletContext = this .getServletContext(); String viewTemplateName = this .getTemplateName(); ISpringTemplateEngine viewTemplateEngine = this .getTemplateEngine(); if (viewTemplateName == null ) { ... } else { Map<String, Object> mergedModel = new HashMap(30 ); Map<String, Object> templateStaticVariables = this .getStaticVariables(); if (templateStaticVariables != null ) { mergedModel.putAll(templateStaticVariables); } if (pathVariablesSelector != null ) { Map<String, Object> pathVars = (Map)request.getAttribute(pathVariablesSelector); if (pathVars != null ) { mergedModel.putAll(pathVars); } } if (model != null ) { mergedModel.putAll(model); } ApplicationContext applicationContext = this .getApplicationContext(); RequestContext requestContext = new RequestContext(request, response, this .getServletContext(), mergedModel); SpringWebMvcThymeleafRequestContext thymeleafRequestContext = new SpringWebMvcThymeleafRequestContext(requestContext, request); addRequestContextAsVariable(mergedModel, "springRequestContext" , requestContext); addRequestContextAsVariable(mergedModel, "springMacroRequestContext" , requestContext); mergedModel.put("thymeleafRequestContext" , thymeleafRequestContext); ConversionService conversionService = (ConversionService)request.getAttribute(ConversionService.class.getName()); ThymeleafEvaluationContext evaluationContext = new ThymeleafEvaluationContext(applicationContext, conversionService); mergedModel.put("thymeleaf::EvaluationContext" , evaluationContext); IEngineConfiguration configuration = viewTemplateEngine.getConfiguration(); WebExpressionContext context = new WebExpressionContext(configuration, request, response, servletContext, this .getLocale(), mergedModel); String templateName; Set markupSelectors; if (!viewTemplateName.contains("::" )) { templateName = viewTemplateName; markupSelectors = null ; } else { IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration); FragmentExpression fragmentExpression; try { fragmentExpression = (FragmentExpression)parser.parseExpression(context, "~{" + viewTemplateName + "}" ); } catch (TemplateProcessingException var25) { throw new IllegalArgumentException("Invalid template name specification: '" + viewTemplateName + "'" ); } FragmentExpression.ExecutedFragmentExpression fragment = FragmentExpression.createExecutedFragmentExpression(context, fragmentExpression); templateName = FragmentExpression.resolveTemplateName(fragment); markupSelectors = FragmentExpression.resolveFragments(fragment); Map<String, Object> nameFragmentParameters = fragment.getFragmentParameters(); if (nameFragmentParameters != null ) { if (fragment.hasSyntheticParameters()) { throw new IllegalArgumentException("Parameters in a view specification must be named (non-synthetic): '" + viewTemplateName + "'" ); } context.setVariables(nameFragmentParameters); } } String templateContentType = this .getContentType(); ... boolean producePartialOutputWhileProcessing = this .getProducePartialOutputWhileProcessing(); Writer templateWriter = producePartialOutputWhileProcessing ? response.getWriter() : new FastStringWriter(1024 ); viewTemplateEngine.process(templateName, processMarkupSelectors, context, (Writer)templateWriter); if (!producePartialOutputWhileProcessing) { response.getWriter().write(templateWriter.toString()); response.getWriter().flush(); } } }
其核心函数process作用为thymeleaf会根据配置的Resolver路径找到模板并解析通过io返回,函数代码如下
try { final long startNanos = System.nanoTime(); final TemplateManager templateManager = this .configuration.getTemplateManager(); templateManager.parseAndProcess(templateSpec, context, writer); final long endNanos = System.nanoTime(); try { writer.flush(); } catch (final IOException e) { throw new TemplateOutputException("An error happened while flushing output writer" , templateSpec.getTemplate(), -1 , -1 , e); }
再跟进 parseAndProcess
public void parseAndProcess (TemplateSpec templateSpec, IContext context, Writer writer) { ... TemplateResolution templateResolution = resolveTemplate(this .configuration, (String)null , template, templateResolutionAttributes, true ); TemplateData templateData = buildTemplateData(templateResolution, template, templateSelectors, templateMode, true ); IEngineContext engineContext = EngineContextManager.prepareEngineContext(this .configuration, templateData, templateResolutionAttributes, context); ProcessorTemplateHandler processorTemplateHandler = new ProcessorTemplateHandler(); ITemplateHandler processingHandlerChain = createTemplateProcessingHandlerChain(engineContext, true , true , processorTemplateHandler, writer); ITemplateParser parser = this .getParserForTemplateMode(engineContext.getTemplateMode()); if (templateResolution.getValidity().isCacheable() && this .templateCache != null ) { ModelBuilderTemplateHandler builderHandler = new ModelBuilderTemplateHandler(this .configuration, templateData); parser.parseStandalone(this .configuration, (String)null , template, templateSelectors, templateData.getTemplateResource(), engineContext.getTemplateMode(), templateResolution.getUseDecoupledLogic(), builderHandler); TemplateModel templateModel = builderHandler.getModel(); this .templateCache.put(cacheKey, templateModel); templateModel.process(processingHandlerChain); } else { parser.parseStandalone(this .configuration, (String)null , template, templateSelectors, templateData.getTemplateResource(), engineContext.getTemplateMode(), templateResolution.getUseDecoupledLogic(), processingHandlerChain); } EngineContextManager.disposeEngineContext(engineContext); }
parser.parseStandalone最终会调用 AbstractMarkupTemplateParser#parse 方法,有两个关键,一个是读取模板,一个是解析模板
Reader templateReader = (resource != null ? resource.reader() : new StringReader(template)); templateReader = new ParserLevelCommentMarkupReader(new PrototypeOnlyCommentMarkupReader(templateReader)); this .parser.parse(templateReader, handler);
参考 https://pdai.tech/md/spring/spring-x-framework-springmvc-source-2.html
https://pdai.tech/files/kaitao-springMVC.pdf
thymeleaf源码解读
SpringMVC源码学习(六)—解析HandlerMethod执行过程_invokehandlermethod
SpringMVC源码学习 | huahua’s Blog