深入源碼分析SpringMVC執行過程

本文主要講解 SpringMVC 執行過程,並針對相關源碼進行解析。

首先,讓我們從 Spring MVC 的四大組件:前端控制器(DispatcherServlet)、處理器映射器(HandlerMapping)、處理器適配器(HandlerAdapter)以及視圖解析器(ViewResolver) 的角度來看一下 Spring MVC 對用戶請求的處理過程,過程如下圖所示:

深入源碼分析SpringMVC執行過程

SpringMVC 執行過程


  1. 用戶請求發送到前端控制器 DispatcherServlet
  2. 前端控制器 DispatcherServlet 接收到請求後,DispatcherServlet 會使用 HandlerMapping 來處理,HandlerMapping 會查找到具體進行處理請求的 Handler 對象
  3. HandlerMapping 找到對應的 Handler 之後,並不是返回一個 Handler 原始對象,而是一個 Handler 執行鏈(HandlerExecutionChain),在這個執行鏈中包括了攔截器和處理請求的 Handler。HandlerMapping 返回一個執行鏈給 DispatcherServlet。
  4. DispatcherServlet 接收到執行鏈之後,會調用 Handler 適配器去執行 Handler
  5. Handler 適配器執行完成 Handler(也就是 Controller)之後會得到一個 ModelAndView,並返回給 DispatcherServlet。
  6. DispatcherServlet 接收到 HandlerAdapter 返回的 ModelAndView 之後,會根據其中的視圖名調用 ViewResolver。
  7. ViewResolver 根據邏輯視圖名解析成一個真正的 View 視圖,並返回給 DispatcherServlet。
  8. DispatcherServlet 接收到視圖之後,會根據上面的 ModelAndView 中的 model 來進行視圖中數據的填充,也就是所謂的
    視圖渲染
  9. 渲染完成之後,DispatcherServlet 就可以將結果返回給用戶了。

在瞭解了大概的執行過程後,讓我們一起去從源碼角度去深入探索(SpringMVC 版本為 5.2.3):

我們先創建一個 Controller 以便進行 debug,內容如下:

<code>@Controller
public class SpringMvcController {
@RequestMapping("testSpringMvc")
public String testSpringMvc(Map<string> map) {
map.put("note", "在看轉發二連");
return "success";
}
}/<string>/<code>

然後再創建一個 html 文件,採用 Thymeleaf 模版引擎,文件內容如下:

<code>



<title>在看轉發二連/<title>




/<code>

好了,然後啟動程序,讓我們訪問 http://localhost:8080/testSpringMvc,來一步一步探索 SpringMVC 的執行過程:

源碼解析

首先當我們訪問頁面的時候,將會把請求發送到前端控制器 DispatcherServlet,DispatcherServlet 是一個 Servlet,我們知道在 Servlet 在處理一個請求的時候會交給 service 方法進行處理,這裡也不例外,DispatcherServlet 繼承了 FrameworkServlet,首先進入 FrameworkServlet 的 service 方法:

<code>protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 請求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 若方法為 PATCH 方法或為空則單獨處理
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {// 其他的請求類型的方法經由父類,也就是 HttpServlet 處理
super.service(request, response);
}
}/<code>

HttpServlet 中會根據請求類型的不同分別調用 doGet 或者 doPost 等方法,FrameworkServlet 中已經重寫了這些方法,在這些方法中會調用 processRequest 進行處理,在 processRequest 中會調用 doService 方法,這個 doService 方法就是在 DispatcherServlet 中實現的。下面就看下 DispatcherServlet 中的 doService 方法的實現。

DispatcherServlet 收到請求

DispatcherServlet 中的 doService方法:

<code>protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// 給 request 中的屬性做一份快照,以便能夠恢復原始屬性

Map<string> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 如果沒有配置本地化或者主題的處理器之類的,SpringMVC 會使用默認的配置文件,即 DispatcherServlet.properties
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, 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);
}

try {
// 開始真正的處理
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// 恢復原始屬性快照
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}/<string>/<code>

接下來 DispatcherServlet 開始真正的處理,讓我們來看下 doDispatch 方法,首先會獲取當前請求的 Handler 執行鏈

,然後找到合適的 HandlerAdapter(此處為 RequestMappingHandlerAdapter),接著調用 RequestMappingHandlerAdapter 的 handle 方法,如下為 doDispatch 方法:

<code>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 {
// 先檢查是不是 Multipart 類型的,比如上傳等;如果是 Multipart 類型的,則轉換為 MultipartHttpServletRequest 類型
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// 獲取當前請求的 Handler 執行鏈
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// 獲取當前請求的 Handler 適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 對於 header 中 last-modified 的處理
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;
}
}

// 遍歷所有定義的 interceptor,執行 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;
}

// 實際調用 Handler 的地方
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 處理成默認視圖名,也就是添加前綴和後綴等
applyDefaultViewName(processedRequest, mv);
// 攔截器postHandle方法進行處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
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()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}/<code>


查找對應的 Handler 對象

讓我們去探索下是如何獲取當前請求的 Handler 執行鏈,對應著這句代碼 mappedHandler = getHandler(processedRequest);,看下 DispatcherServlet 具體的 getHandler 方法,該方法主要是遍歷所有的 handlerMappings 進行處理,handlerMappings 是在啟動的時候預先註冊好的,handlerMappings 包含 RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、RouterFunctionMapping、SimpleUrlHandlerMapping 以及 WelcomePageHandlerMapping,在循環中會調用 AbstractHandlerMapping 類中的 getHandler 方法來獲取 Handler 執行鏈,若獲取的 Handler 執行鏈不為 null,則返回當前請求的 Handler 執行鏈,DispatcherServlet 類的 getHandler 方法如下:

<code>protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍歷所有的 handlerMappings 進行處理,handlerMappings 是在啟動的時候預先註冊好的
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}/<code>

在循環中,根據 mapping.getHandler(request);,繼續往下看 AbstractHandlerMapping 類中的 getHandler 方法

<code>public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 根據 request 獲取 handler
Object handler = getHandlerInternal(request);
if (handler == null) {
// 如果沒有找到就使用默認的 handler

handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// 如果 Handler 是 String,表明是一個 bean 名稱,需要尋找對應 bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 封裝 Handler 執行鏈
return getHandlerExecutionChain(handler, request);
}/<code>

AbstractHandlerMapping 類中的 getHandler 方法中首先根據 requrst 獲取 handler,主要是調用了 AbstractHandlerMethodMapping 類中的 getHandlerInternal 方法,該方法首先獲取 request 中的 url,即 /testSpringMvc,用來匹配 handler 並封裝成 HandlerMethod,然後根據 handlerMethod 中的 bean 來實例化 Handler 並返回。

<code>protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 獲取 request 中的 url,用來匹配 handler
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
// 根據路徑尋找 Handler,並封裝成 HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 根據 handlerMethod 中的 bean 來實例化 Handler,並添加進 HandlerMethod
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}/<code>

接下來,我們看 lookupHandlerMethod

的邏輯,主要邏輯委託給了 mappingRegistry 這個成員變量來處理:

<code>protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<match> matches = new ArrayList<>();
// 通過 lookupPath 屬性中查找。如果找到了,就返回對應的RequestMappingInfo
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 如果匹配到了,檢查其他屬性是否符合要求,如請求方法,參數,header 等
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 沒有直接匹配到,則遍歷所有的處理方法進行通配符匹配
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}

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();
// 不能有相同的最優 Match
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 設置 request 參數(RequestMappingHandlerMapping 對其進行了覆寫)
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回匹配的 url 的處理的方法
return bestMatch.handlerMethod;
}
else {
// 調用 RequestMappingHandlerMapping 類的 handleNoMatch 方法再匹配一次
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}/<match>
/<match>/<code>

通過上面的過程,我們就獲取到了 Handler,就開始封裝執行鏈了,就是將我們配置的攔截器加入到執行鏈中去,getHandlerExecutionChain 方法如下:

<code>protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 如果當前 Handler 不是執行鏈類型,就使用一個新的執行鏈實例封裝起來
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
// 遍歷攔截器,找到跟當前 url 對應的,添加進執行鏈中去
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());

}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}/<code>

到此為止,我們就獲取了當前請求的 Handler 執行鏈,接下來看下是如何獲取請求的 Handler 適配器,主要依靠 DispatcherServlet 類的 getHandlerAdapter 方法,該方法就是遍歷所有的 HandlerAdapter,找到和當前 Handler 匹配的就返回,在這裡匹配到的為 RequestMappingHandlerAdapter。DispatcherServlet 類的 getHandlerAdapter 方法如下:

<code>protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍歷所有的 HandlerAdapter,找到和當前 Handler 匹配的就返回
for (HandlerAdapter adapter : this.handlerAdapters) {
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");
}/<code>


HandlerAdapter 執行當前的 Handler


再獲取完當前請求的 Handler 適配器後,接著進行緩存處理

,也就是對 last-modified 的處理,然後調用 applyPreHandle 方法執行攔截器的 preHandle 方法,即遍歷所有定義的 interceptor,執行 preHandle 方法,然後就到了實際執行 handle 的地方,doDispatch 方法中 handle 方法是執行當前 Handler,我們這裡使用的是 RequestMappingHandlerAdapter,首先會進入 AbstractHandlerMethodAdapter 的 handle 方法

<code>public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}/<code>

在 AbstractHandlerMethodAdapter 的 handle 方法中又調用了 RequestMappingHandlerAdapter 類的 handleInternal 方法

<code>protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 執行方法,封裝 ModelAndView
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}

}
return mav;
}/<code>

在執行完 handle 方法後,然後調用 applyDefaultViewName 方法組裝默認視圖名稱,將前綴和後綴名都加上,接著調用 applyPostHandle 方法執行攔截器的 preHandle 方法,也就是遍歷所有定義的 interceptor,執行preHandle 方法。

處理最終結果以及渲染

最後調用 DispatcherServlet 類中的 processDispatchResult 方法,此方法是處理最終結果的,包括異常處理、渲染頁面和發出完成通知觸發攔截器的 afterCompletion() 方法執行等,processDispatchResult()方法代碼如下:

<code>private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
}
}/<code>

接下來讓我們看下 DispatcherServlet 類的 render 方法是如何完成渲染的,DispatcherServlet 類的 render 方法渲染過程如下:

  1. 判斷 ModelAndView 中 view 是否為 view name,沒有獲取其實例對象:如果是根據 name,如果是則需要調用 resolveViewName 從視圖解析器獲取對應的視圖(View)對象;否則 ModelAndView 中使用 getview 方法獲取 view 對象。
  2. 然後調用 View 類的 render 方法。

DispatcherServlet 類的 render 方法如下:

<code>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);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");

}
}
else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}

if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 委託給視圖進行渲染
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}/<code>

因為我們用的是 Thymeleaf 模版引擎,所以 view.render 找到對應的視圖 ThymeleafView 的 render 方法進行渲染。

<code>public void render(final Map<string> model, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
renderFragment(this.markupSelectors, model, request, response);
}/<string>/<code>

ThymeleafView 的 render 方法又調用 renderFragment 方法進行視圖渲染,渲染完成之後,DispatcherServlet 就可以將結果返回給我們了。

也就是 note 對應的 value:在看轉發二連

深入源碼分析SpringMVC執行過程

總結

通過本文的源碼分析,我相信我們都能夠清楚的認識到 SpringMVC 執行流程,進一步加深對 SpringMVC 的理解。

參考

https://dwz.cn/DmzEGQcx

https://dwz.cn/MSB2GJKT


分享到:


相關文章: