DispatcherServlet流程简要分析

logo

基本概念

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,返回给客户端。

流程图

image-20241010171153287

首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控制

DispatcherServlet——>HandlerMapping, HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一 个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新 的映射策略

DispatcherServlet——>HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器

HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处 理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名)

ModelAndView 的逻辑视图名——> ViewResolver,ViewResolver 将把逻辑视图名解析为具体的View,通过这种策 略模式,很容易更换其他视图技术

View——>渲染,View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此 很容易支持其他视图技术

返回控制权给DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束

image-20241010160411868

调试分析

控制器

@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());
// 初始化context
this.initContextHolders(request, localeContext, requestAttributes);

try {
// doService
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 {
// 重置context
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 {
// 核心 doDispatch分发request请求
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 {
// 判断是不是文件上传类型的request
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;

// 根据request获取匹配的handler
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}

// 根据handler获取匹配的handlerAdapter
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;
}

// 真正handle处理,并返回modelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 通过视图的prefix和postfix获取完整的视图名
this.applyDefaultViewName(processedRequest, mv);

// 应用后置的拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}

// 处理handler处理的结果,显然就是对ModelAndView 或者 出现的Excpetion处理
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

} catch (Exception var22) {
// 对页面渲染完成里调用拦截器中的AfterCompletion方法
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();
// 核心方法getHandler
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

这个handlerMappings:处理器映射,保存了每一个处理器可以处理哪些请求的方法的映射信息

  • RequestMappingHandlerMapping:基于注解,如@RequestMapping的请求映射
  • WelcomePageHandlerMapping:欢迎页面
  • BeanNameUrlHandlerMapping:根据 Bean 的名称来处理 URL 请求映射
  • RouterFunctionMapping:用于处理基于函数式编程风格的请求映射,而不是基于注解的方式
  • SimpleUrlHandlerMapping:简单的URL到控制器的直接映射,通常通过配置文件如application.properties

image-20241010180329266

其中的 RequestMappingHandlerMapping 在内存马的学习中已经见到过,其保存的就是关于请求的信息

那么 RequestMappingHandlerMapping['mappingRegistry']['registry'] 存储的就是对应的 @Controller 信息

image-20241010182137640

跟进到核心方法:AbstractHandlerMapping#getHandler

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取URI对应的方法
Object handler = this.getHandlerInternal(request);
if (handler == null) {
handler = this.getDefaultHandler();
}

if (handler == null) {
return null;
} else {
// Bean名称或解析处理程序
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.obtainApplicationContext().getBean(handlerName);
}

if (!ServletRequestPathUtils.hasCachedPath(request)) {
this.initLookupPath(request);
}
// 获取HandlerExecutionChain
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 {
// 获取请求的URI路径
String lookupPath = this.initLookupPath(request);
this.mappingRegistry.acquireReadLock();

HandlerMethod var4;
try {
// 根据URI路径从mappingRegistry中获取对应的处理方法
HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
} finally {
this.mappingRegistry.releaseReadLock();
}
return var4;
}

根据请求路径获取对应的处理方法(即自己写的controller对应的函数)

image-20241011102807020

二、跟进AbstractHandlerMapping#getHandlerExecutionChain

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 创建chain,添加处理方法
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);
}
}
// 返回chain对象
return chain;
}

将handler作为初始参数,创建HandlerExecutionChain对象,然后遍历 adaptedInterceptors 集合,该集合里存放的都是拦截器,如果拦截器的类型是 MappedInterceptor,则调用 matches 方法去匹配一下,看一下是否是拦截当前请求的拦截器,如果是,则调用 chain.addInterceptor 方法加入到 HandlerExecutionChain 对象中;如果就是一个普通拦截器,则直接加入到 HandlerExecutionChain 对象中

image-20241011104209327

通过一、二成功获取到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注解所取代

image-20241011115428439

这里获取的就是 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 方法

image-20241011121055925

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);
/* http session 相关*/
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// 核心函数 invokeHandlerMethod
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 --> 工厂类,为目标对象创建一个WebDataBinder实例
// 1.WebDataBinder继承了DataBinder类,为web请求提供了:参数绑定服务
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

// 获取ModelFactory:
// 2.主要是两个功能,1是在处理器具体处理之前对model进行初始化,2是在处理完请求后对model参数进行更新
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

// 3.ServletInvocableHandlerMethod继承并扩展了InvocableHandlerMethod
// 实际的请求处理、参数绑定、处理请求以及返回值处理 都在此处完成;核心对象
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

// 4.四个set方法对invocableMethod设置属性
// 设置参数处理器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 设置返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 设置参数绑定工厂
invocableMethod.setDataBinderFactory(binderFactory);
// 设置参数名称发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

// 5.创建ModelAndViewContainer,并初始化Model对象,用于保存model和View对象
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 将flashmap中的数据设置到model中
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 使用modelFactory将sessionAttributes和注释了@ModelAttribute的方法的参数设置到model中
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 根据配置对ignoreDefaultModelOnRedirect进行设置
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

// 6.异步请求相关
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);
}

// 7.调用Controller中的具体方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

// 8.返回ModelAndView对象
// 处理完请求后的后置处理
// 调用ModelFactory的updateModel方法更新model,包括设置SessionAttribute和给Model设置BinderResult
// 根据mavContainer创建了ModelAndView
// 如果mavContainer里的model是RedirectAttributes类型,则将其设置到FlashMap
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 完成请求后续处理,并将当前请求置为未激活
webRequest.requestCompleted();
}
}

最主要是为以下几步

  • 绑定参数
  • 创建ModelAndViewContainer对象
  • 处理器调用
  • 从ModelAndViewContainer中取出ModelAndView对象

有错别字的流程图如下

2

ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// controller对应的函数的返回值
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);
}......
}

image-20241012115331479

this.returnValueHandlers.handleReturnValue 处理返回值时,设置controller的return值作为view属性

if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}

image-20241012220502511

最后通过 getModelAndView 获取 ModelAndView 对象

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
// 获取Model对象
ModelMap model = mavContainer.getModel();
// 创建ModelAndView对象,包含model和view
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
// 判断是不是Redirect
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

image-20241012221148429

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

// Did the handler return a view to render?
// 核心函数 render
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()) {
// Concurrent handling started during a forward
return;
}

if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

render

跟进核心函数:DispatcherServlet#render

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
// 国际化设置
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);

View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 核心函数:resolveViewName,获取最匹配的视图解析器
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
...
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
...
}
...
try {
...
// 核心函数:render,调用视图解析器去渲染
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是自己多加的)

image-20241012231003458

在众多视图解析器中,首先进来的是 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中

image-20241013000147650

最后返回两个

image-20241013000626012

2、之后通过 getBestView 拿到最适配的解析器,getBestView中的逻辑是优先返回在 candidateViews 存在重定向动作的 view,接着判断ContentType是否匹配返回最适配的 View

private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) {
// 是否是存在Redirect的View
for (View candidateView : candidateViews) {
if (candidateView instanceof SmartView) {
SmartView smartView = (SmartView) candidateView;
if (smartView.isRedirectView()) {
return candidateView;
}
}
}
// 根据ContentType是否匹配
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)

image-20241013001647040

进入其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 servletContext = this.getServletContext();
String viewTemplateName = this.getTemplateName();
//获取SpringTemplateEngine引擎
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 applicationContext = this.getApplicationContext();
//requestContext
RequestContext requestContext = new RequestContext(request, response, this.getServletContext(), mergedModel);
//thymeleafRequestContext
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());
//evaluationContext
ThymeleafEvaluationContext evaluationContext = new ThymeleafEvaluationContext(applicationContext, conversionService);
mergedModel.put("thymeleaf::EvaluationContext", evaluationContext);

// 获取SpringTemplateEngine配置文件
IEngineConfiguration configuration = viewTemplateEngine.getConfiguration();
//通过前面众多context上下文构造出WebExpressionContext
WebExpressionContext context = new WebExpressionContext(configuration, request, response, servletContext, this.getLocale(), mergedModel);

String templateName;
Set markupSelectors;

//重点:判断模板文件名(return值)是否包含 `::`
if (!viewTemplateName.contains("::")) {
templateName = viewTemplateName;
markupSelectors = null;
} else {
// 这里是SSTI漏洞触发点之一,这里先略过
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();
//一些关于ContentType和Encoding的操作
...

boolean producePartialOutputWhileProcessing = this.getProducePartialOutputWhileProcessing();
Writer templateWriter = producePartialOutputWhileProcessing ? response.getWriter() : new FastStringWriter(1024);
// 核心函数process,thymeleaf便会根据配置的Resolver路径找到模板并解析通过io返回
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();

// 2.找到模板并且处理
templateManager.parseAndProcess(templateSpec, context, writer);

final long endNanos = System.nanoTime();
try {
// 3.io刷新到请求页面
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 templateData = buildTemplateData(templateResolution, template, templateSelectors, templateMode, true);
//处理request与response放入到IEngineContext中,处理varilable
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 方法,有两个关键,一个是读取模板,一个是解析模板

// 读取模板,会调用之前绑定的resource解析器
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