Spring中@ExceptionHandler注解的工作原理詳解
@ExceptionHandler注解工作原理
我們知道,Spring Web注解@ExceptionHandler可以用來(lái)指定處理某類異常的控制器方法,從而在這些異常發(fā)生時(shí),會(huì)有相應(yīng)的控制器方法來(lái)處理此類異常,其定義方式如下 :
/** * 此方法定義一個(gè)異常處理器,僅僅處理異常 DemoException , 它使用一個(gè)視圖對(duì)象 * DemoExceptionHandlerView 來(lái)處理異常 * @param e * @return */ @ExceptionHandler(DemoException.class) public View handleDemoException(DemoException e) { return new DemoExceptionHandlerView(e); }
另外,通過(guò)@ExceptionHandler定義異常處理控制器方法,又可以分為兩類 :
- 定義在某個(gè)控制類內(nèi),這種情況下,所定義的異常處理控制器方法僅僅覆蓋當(dāng)前控制器類內(nèi)各個(gè)方法所發(fā)生的這類異常
- 結(jié)合@ControllerAdvice使用,定義在@ControllerAdvice注解的類內(nèi),這種情況下,所定義的異常處理控制器方法可用于@ControllerAdvice注解覆蓋的所有控制器類方法內(nèi)所發(fā)生的這類異常
那么,在這種現(xiàn)象背后,Spring MVC又是如何實(shí)現(xiàn)的呢 ? 實(shí)際上,這主要是 ExceptionHandlerExceptionResolver在起作用 :
首先,ExceptionHandlerExceptionResolver是Spring MVC缺省被啟用的一個(gè)HandlerExceptionResolver,它會(huì)被作為一個(gè)組合模式HandlerExceptionResolver bean中的一個(gè)元素進(jìn)入到bean容器中。
ExceptionHandlerExceptionResolver實(shí)現(xiàn)了接口InitializingBean,所以它在實(shí)例化時(shí)會(huì)被初始化。該過(guò)程中,它就會(huì)搜集所有的@ControllerAdvice注解類中使用@ExceptionHandler定義的異常處理控制器方法以供隨后工作時(shí)使用。
接下來(lái),DispatcherServlet初始化時(shí),會(huì)搜集所有HandlerExceptionResolver bean記錄到自己的策略組件屬性List<HandlerExceptionResolver> handlerExceptionResolvers。
然后,處理某個(gè)請(qǐng)求時(shí)某個(gè)異常發(fā)生了。DispatcherServlet會(huì)遍歷handlerExceptionResolvers中每個(gè)HandlerExceptionResolver對(duì)象試圖對(duì)該異常進(jìn)行處理。
// DispatcherServlet 代碼片段, // 在 HandlerAdaptor 執(zhí)行 Handler 之后調(diào)用, // 如果之前的邏輯有異常,則 exception 不為 null, // 如果之前的邏輯執(zhí)行正常, 則 exception 為 null private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; if (exception != null) { // 請(qǐng)求處理出現(xiàn)了異常 if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); // 這里視圖處理該異常: // 1. 內(nèi)部將其完全處理 // 2. 或者返回一個(gè)用于渲染錯(cuò)誤的數(shù)據(jù)模型和視圖 mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // ... 省略其他代碼 } @Nullable protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { // Success and error responses may use different content types request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; if (this.handlerExceptionResolvers != null) { // 遍歷`handlerExceptionResolvers`中每個(gè)`HandlerExceptionResolver`對(duì)象 // 試圖對(duì)異常 ex 進(jìn)行處理 for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } } if (exMv != null) { // ... return exMv; } throw ex; }
這里,輪到ExceptionHandlerExceptionResolver處理異常時(shí),#resolveException最終會(huì)調(diào)用ExceptionHandlerExceptionResolver#doResolveHandlerMethodException。該處理過(guò)程主要步驟如下 :
找到能處理該異常的控制器方法 – ExceptionHandlerExceptionResolver#getExceptionHandlerMethod; 先從發(fā)生異常的控制器方法所在類查找是否存在使用注解@ExceptionHandler并能處理該異常的方法;如果找不到,從所有@ControllerAdvice注解類中查找使用注解@ExceptionHandler并能處理該異常的方法; 執(zhí)行處理該異常的控制器方法處理該異常;
到此這篇關(guān)于Spring中@ExceptionHandler注解的工作原理詳解的文章就介紹到這了,更多相關(guān)@ExceptionHandler注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring中的@ExceptionHandler注解統(tǒng)一異常處理詳解
- SpringMVC使用@ExceptionHandler注解在Controller中處理異常
- Spring的異常處理@ExceptionHandler注解解析
- 關(guān)于SpringBoot使用@ExceptionHandler注解局部異常處理
- Spring中@ExceptionHandler注解的使用方式
- Spring @ExceptionHandler注解統(tǒng)一異常處理和獲取方法名
- Spring中的@ControllerAdvice和@ExceptionHandler注解處理全局異常
- Spring中的@ExceptionHandler注解詳解與應(yīng)用示例
相關(guān)文章
Java編程Socket實(shí)現(xiàn)多個(gè)客戶端連接同一個(gè)服務(wù)端代碼
這篇文章主要介紹了Java編程Socket實(shí)現(xiàn)多個(gè)客戶端連接同一個(gè)服務(wù)端代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Mybatis中foreach標(biāo)簽帶來(lái)的空格\換行\(zhòng)回車問(wèn)題及解決方案
這篇文章主要介紹了解決Mybatis中foreach標(biāo)簽帶來(lái)的空格,換行,回車問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04淺析javax.servlet.Servlet,ServletContext接口
本篇文章是對(duì)javax.servlet.Servlet,ServletContext接口進(jìn)行了纖細(xì)的分析介紹,需要的朋友參考下2013-07-07Spring之AOP兩種代理機(jī)制對(duì)比分析(JDK和CGLib動(dòng)態(tài)代理)
這篇文章主要介紹了Spring之AOP兩種代理機(jī)制對(duì)比分析(JDK和CGLib動(dòng)態(tài)代理),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05Spring創(chuàng)建Bean的過(guò)程Debug的詳細(xì)流程
這篇文章主要介紹了Spring創(chuàng)建Bean的過(guò)程Debug的流程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11Java中Vector與ArrayList的區(qū)別詳解
本篇文章是對(duì)Java中Vector與ArrayList的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06Java Runnable線程傳參,實(shí)現(xiàn)讓run訪問(wèn)參數(shù)
這篇文章主要介紹了Java Runnable線程傳參,實(shí)現(xiàn)讓run訪問(wèn)參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09