SpringBoot一個請求的處理全過程分享
平時只是在用SpringBoot框架,但并沒有詳細研究過請求執(zhí)行的一個具體過程,所以本文主要來梳理一下SpringBoot請求處理的全過程。
請求處理流程圖
容器包含關系圖
請求簡要流程圖
請求詳細流程圖
請求處理流程詳解
請求處理主要流程
- 過濾器鏈chain.doFilter之前的執(zhí)行(過濾器是在Server容器中的,如Tomcat);
- 攔截器鏈preHandle方法執(zhí)行;
- 路徑映射、參數(shù)綁定(參數(shù)解析、參數(shù)轉(zhuǎn)換、參數(shù)校驗);
- Controller的具體方法執(zhí)行;
- 返回值處理(含信息轉(zhuǎn)換);
- 攔截器鏈postHandle方法執(zhí)行;
- 異常解析器處理異常(@ControllerAdvice、自定義異常解析器都在這里執(zhí)行);
- 視圖解析渲染;
- 攔截器鏈afterCompletion方法執(zhí)行;
- 過濾器鏈chain.doFilter之后的執(zhí)行。
請求處理詳細流程
Tomcat線程接受到請求,經(jīng)過一系列調(diào)用后,調(diào)用到ApplicationFilterChain的doFilter方法。doFilter方法調(diào)用ApplicationFilterChain的internalDoFilter方法,依次執(zhí)行過濾器鏈的每個Filter的doFilter。
過濾器鏈的所有doFIlter執(zhí)行完畢, 控制權交回ApplicationFilterChain, 經(jīng)過一系列調(diào)用后,調(diào)用到DispatcherServlet的doDispatch方法。
doDispatch方法的主要流程:
- DispatcherServlet的getHandler方法:得到處理執(zhí)行器鏈(包含處理器和攔截器鏈)。
- DispatcherServlet的getHandlerAdapter方法:得到處理器適配器。
- HandlerExecutionChain的applyPreHandle方法:執(zhí)行執(zhí)行器鏈中的所有攔截器方法preHandle。
AbstractHandlerMethodAdapter的handle方法:該方法主要包含路徑映射、參數(shù)bangd (參數(shù)解析、參數(shù)轉(zhuǎn)換、參數(shù)校驗)、調(diào)用具體控制器方法、返回值處理(含信息轉(zhuǎn)換)等操作。
handle方法的主要流程:
- 調(diào)用RequestMappingHandlerAdapter的handleInternal方法
- handleInternal方法又調(diào)用RequestMappingHandlerAdapter的invokeHandlerMethod方法
invokeHandlerMethod方法的主要流程:
- 調(diào)用RequestMappingHandlerAdapter的createInvocableHandlerMethod方法:注冊參數(shù)解析器、返回值處理器、信息轉(zhuǎn)化器等到ServletInvocableHandlerMethod 對象實例中。
- 調(diào)用ServletInvocableHandlerMethod的invokeAndHandle方法。
invokeAndHandle方法的主要流程:
- 調(diào)用InvocableHandlerMethod的invokeForRequest方法
- invokeForRequest方法又調(diào)用InvocableHandlerMethod的doInvoke方法
doInvoke方法的主要流程:
- 調(diào)用InvocableHandlerMethod的getMethodArgumentValues方法:路徑映射、參數(shù)綁定(參數(shù)解析、參數(shù)轉(zhuǎn)換、參數(shù)校驗)。
- 調(diào)用Method的invoke方法,內(nèi)部調(diào)用DelegatingMethodAccessorImpl的invoke方法,內(nèi)部調(diào)用InvocableHandlerMethod的doInvoke方法,內(nèi)部調(diào)用NativeMethodAccessorImpl的invoke方法,內(nèi)部調(diào)用NativeMethodAccessorImpl的invoke0方法,內(nèi)部調(diào)用具體Controller的具體方法,得到響應結(jié)果。
- 調(diào)用HandlerMethodReturnValueHandlerComposite的handleReturnValue方法:返回值處理(含信息轉(zhuǎn)換)。
- 調(diào)用HandlerExecutionChain的applyPostHandle方法:執(zhí)行執(zhí)行器鏈中的所有攔截方法postHandle。
- 調(diào)用DispatcherServlet的processDispatchResult方法。
processDispatchResult方法的主要流程:
- 調(diào)用DispatcherServlet的processHandlerException方法:異常處理(獲取合適的異常解析器處理異常信息,@ControllerAdvice全局異常處理和自定義異常解析器都是在這一步執(zhí)行的)。
- 調(diào)用DispatcherServlet的render方法:視圖解析渲染。
- 調(diào)用HandlerExecutionChain的triggerAfterCompletion方法:執(zhí)行執(zhí)行器鏈中的所有攔截方法afterCompletion。
- 控制權交回ApplicationFilterChain , 繼續(xù)執(zhí)行過濾器鏈的所有doFIlter之后的代碼。
常見問題
全局異常處理失效
1.@ControllerAdvice作用范圍
攔截器的preHandle方法、postHandle方法拋出的異常在ControllerAdvice處理范圍內(nèi),但攔截器的afterCompletion方法拋出的異常不在處理范圍內(nèi)(攔截器的afterCompletion拋出的異常會被直接catch處理,不會往外拋出異常,只會打印錯誤日志)。
HandlerExecutionChain類源碼
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }
過濾器拋出的異常不在ControllerAdvice處理范圍內(nèi)。要處理過濾器拋出的異常,可以自定義Filter并放在過濾鏈的最前面,catch處理異常。
2.多個@ControllerAdvice優(yōu)先級問題
- 多個使用@ControllerAdvice 的Bean按執(zhí)行順序(通過Order注解設置執(zhí)行順序,值越小月優(yōu)先執(zhí)行)依次執(zhí)行
- 當某個Bean的方法匹配上異常時,進行異常處理,直接返回,后續(xù)的方法和Bean不在執(zhí)行。
關于全局異常處理詳細講解可參考文章:[SpringBoot全局異常處理]
后續(xù)文章會繼續(xù)針對請求流程里面的一些主要功能作深入探討,如:過濾器、攔截器、參數(shù)解析、參數(shù)轉(zhuǎn)換、參數(shù)校驗、返回值處理、異常處理等方面。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SSM框架通過mybatis-generator自動生成代碼(推薦)
這篇文章主要介紹了SSM框架通過mybatis-generator自動生成代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2017-11-11Java解析DICOM圖之如何獲得16進制數(shù)據(jù)詳解
DICOM就是醫(yī)學數(shù)字成像和通信,是醫(yī)學圖像和相關信息的國際標準(ISO 12052),下面這篇文章主要給大家介紹了關于Java解析DICOM圖之如何獲得16進制數(shù)據(jù)的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。2017-10-10Java數(shù)據(jù)結(jié)構之KMP算法詳解以及代碼實現(xiàn)
KMP算法是一種改進的字符串匹配算法,核心是利用之前的匹配失敗時留下的信息,選擇最長匹配長度直接滑動,從而減少匹配次數(shù)。本文主要介紹了KMP算法的原理與實現(xiàn),需要的可以參考一下2022-12-12Springboot基于BCrypt非對稱加密字符串的實現(xiàn)
本文主要介紹了Springboot基于BCrypt非對稱加密字符串的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04關于Spring中的@Configuration中的proxyBeanMethods屬性
這篇文章主要介紹了關于Spring中的@Configuration中的proxyBeanMethods屬性,需要的朋友可以參考下2023-07-07Java設計模式之抽象工廠模式(Abstract?Factory)
這篇文章主要為大家詳細介紹了Java設計模式之抽象工廠模式,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03