SpringMVC 4.3 源碼分析之 HandlerExceptionResolver

1. HandlerExceptionResolver 概述

HandlerExceptionResolver: 在處理 handler 映射 或執(zhí)行 HandlerExecutionChain/Handler 拋出的異常信息, 一般是返回對應的 ModelAndView(PS: 設置 ViewName 與 Model 中的屬性信息), 其主接口如下:

// 針對 通過 path 獲取 handler 或 執(zhí)行handler時出現(xiàn)異常的處理類
public interface HandlerExceptionResolver {
    // 通過解析異常查詢配置以得到符合條件的 ModelAndView 對象
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}

其主要實現(xiàn)類如下:

1. SimpleMappingExceptionResolver
    在配置信息中配置 異常Class <-> ViewName的映射的 異常處理器
2. ResponseStatusExceptionResolver
    根據(jù)異常 class 上面的 @ResponseStatus 中注解的 Http Code 與 reson 進行返回 ModeAndView
3. DefaultHandlerExceptionResolver
    默認異常解析器, 進行異常的類型進行相應的解析操作, 在 Http 頭部設置對應的 Code, 最后返回對應的 ModelAndView
4. ExceptionHandlerExceptionResolver
    通過激活在 方法上注釋 @ExceptionHandler 注解的方法來處理對應的異常, 激活的操作通過 InvocableHandlerMethod 來進行操作 (Invocable 是 invoke的變形)
5. HandlerExceptionResolverComposite
    異常處理的組合模式類, 通過刷選滿足條件的 ExceptionResolver, 只要有能解決這個異常的, 則直接返回
2. 簡單異常處理類 SimpleMappingExceptionResolver

在這個類中主要有個存儲異常類型 class <-> ViewName 的 Properties, 存儲 viewName <-> HttpCode的Properties, 當出現(xiàn)異常了, 就通過這個 properties 進行獲取 viewName, 并通過 viewName 獲取對應的 HttpCode; 主要有如下屬性:

// 配置 異常 Class <--> ViewName 映射關系的 Property 的文件
private Properties exceptionMappings;
// 忽略的異常的類型
private Class<?>[] excludedExceptions;
// Set the name of the default error view 設置默認的 錯誤頁面
private String defaultErrorView;
// 默認的 Http 返回的狀態(tài)值
private Integer defaultStatusCode;
// 這里配置的是 ViewName <--> Http Code 的映射關系
private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
3. 根據(jù) Exception 上的注解 ResponseStatus 設置httpCode 的ResponseStatusExceptionResolver

這個異常處理類比較簡單, 主要是出了異常, 則根據(jù) Exception 上的注解@ResponseStatus 來獲取 HttpCode 與 ErrorMsg, 而返回的是一個空的 ModelAndView

4. 默認的異常處理類 DefaultHandlerExceptionResolver

這是一個根據(jù)異常的類型設置 http code 與 ErrorMsg 的 異常處理類, 主要處理的異常如下:

1. 處理 NoSuchRequestHandlingMethodException, 設置 http code 404 --> SC_NOT_FOUND
2. 處理 HttpRequestMethodNotSupportedException, 設置 http code 405 -- > SC_METHOD_NOT_ALLOWED
3. 處理 HttpMediaTypeNotSupportedException, 設置 http code 415 --> SC_UNSUPPORTED_MEDIA_TYPE
4. 處理 HttpMediaTypeNotAcceptableException, 設置 http code 406 --> SC_NOT_ACCEPTABLE
5. 處理 MissingPathVariableException, 設置 http code 500 --> SC_INTERNAL_SERVER_ERROR
6. 處理 MissingServletRequestParameterException, 設置 http code 400 --> SC_BAD_REQUEST
7. 處理 ServletRequestBindingException, 設置 http code 400 --> SC_BAD_REQUEST
8. 處理 ConversionNotSupportedException, 設置 http code 500 --> SC_INTERNAL_SERVER_ERROR
9. 處理 TypeMismatchException, 設置 http code 400 --> SC_BAD_REQUEST
10. 處理 HttpMessageNotReadableException, 設置 http code 400 --> SC_BAD_REQUEST
11. 處理 HttpMessageNotWritableException, 設置 http code 500 --> SC_INTERNAL_SERVER_ERROR
12. 處理 MethodArgumentNotValidException, 設置 http code 400 --> SC_BAD_REQUEST
13. 處理 MissingServletRequestPartException, 設置 http code 400 --> SC_BAD_REQUEST
14. 處理 BindException, 設置 http code 400 --> SC_BAD_REQUEST
15. 處理 NoHandlerFoundException, 設置 http code 404 --> SC_NOT_FOUND
16. 處理 AsyncRequestTimeoutException, 設置 http code 503 --> SC_SERVICE_UNAVAILABLE
5. 基于注解@ExceptionHandler 的異常處理類 ExceptionHandlerExceptionResolver

其實就是在指定的方法上標注 @ExceptionHandler 最后通過 InvocableHandlerMethod 進行激活方法 <-- 這個方法就是處理異常的; 其主要有如下屬性:

// HandlerMethod 參數(shù)解析器
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
// 組合模式的 HandlerMethod 參數(shù)解析器
private HandlerMethodArgumentResolverComposite argumentResolvers;
// HandlerMethod 返回值處理器
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
// 組合模式的 HandlerMethod 返回值處理器
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
// Http 消息轉換器
private List<HttpMessageConverter<?>> messageConverters;
// MediaType 解決器(PS: 根據(jù)請求 uri 尾綴, 或 Header 中的信息, 來決定 MediaType)
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
// 對 Response 進行增強的 Advice
private final List<Object> responseBodyAdvice = new ArrayList<Object>();
// IOC 工廠類
private ApplicationContext applicationContext;
// 緩存異常處理類 key: 包含處理異常的方法, value: ExceptionHandlerMethodResolver <-- 這里包含了 Exception <--> method 的鍵值對
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<Class<?>, ExceptionHandlerMethodResolver>(64);
// 被 @ControllerAdvice 注解修飾的的處理類, 再被包裝成 ExceptionHandlerMethodResolver
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap<ControllerAdviceBean, ExceptionHandlerMethodResolver>();

而對應的激活方法主要如下:

// 這里是獲取 能處理異常 Exception 的handler, 并且封裝成 ServletInvocableHandlerMethod
// (PS: InvocableHandlerMethod 主要是激活方法, 并且通過 HandlerMethodReturnValueHandler 來處理一下返回值, 這里的參數(shù) providedArgs 是首選參數(shù), 比如說異常處理的 InvocableHandlerMethod, 這時會傳來參數(shù) Exception, cause)
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) return null;
// 給 ServletInvocableHandlerMethod 設置 argumentResolver
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
// 給 ServletInvocableHandlerMethod 設置 returnValueHandler
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);  
// 封裝統(tǒng)一的 Request 類
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();

try {
    logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
    Throwable cause = exception.getCause();
    if (cause != null) { // Expose cause as provided argument as well // 激活處理異常的方法 (首選參數(shù)是 exception, cause)
        exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
    } else { // 若 cause == null, 則只傳遞 exception
        exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
    }
}catch (Throwable invocationEx) {
    return null;
}
6. 總結

總體上 HandlerExceptionResolver 的設計體系和HandlerMethodArgumentResolver 差不多, 設計上夾雜著策略, 模版, 適配器 等等, 而我們常用的 @ ExceptionHandler 就是通過ExceptionHandlerExceptionResolver 來解決的(PS: 將異常信息及Cause 作為參數(shù)傳入 InvocableHandlerMethod, 最終激活方法)!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子食拜,更是在濱河造成了極大的恐慌,老刑警劉巖摘投,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異虹蓄,居然都是意外死亡谷朝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門武花,熙熙樓的掌柜王于貴愁眉苦臉地迎上來圆凰,“玉大人,你說我怎么就攤上這事体箕∽ǘぃ” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵累铅,是天一觀的道長跃须。 經(jīng)常有香客問我,道長娃兽,這世上最難降的妖魔是什么菇民? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上第练,老公的妹妹穿的比我還像新娘阔馋。我一直安慰自己,他們只是感情好娇掏,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布呕寝。 她就那樣靜靜地躺著,像睡著了一般婴梧。 火紅的嫁衣襯著肌膚如雪下梢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天塞蹭,我揣著相機與錄音孽江,去河邊找鬼。 笑死番电,一個胖子當著我的面吹牛竟坛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钧舌,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涎跨!你這毒婦竟也來了洼冻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤隅很,失蹤者是張志新(化名)和其女友劉穎撞牢,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叔营,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡屋彪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绒尊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畜挥。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖婴谱,靈堂內(nèi)的尸體忽然破棺而出蟹但,到底是詐尸還是另有隱情,我是刑警寧澤谭羔,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布华糖,位于F島的核電站,受9級特大地震影響瘟裸,放射性物質(zhì)發(fā)生泄漏客叉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兼搏。 院中可真熱鬧卵慰,春花似錦、人聲如沸向族。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽件相。三九已至再扭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間夜矗,已是汗流浹背泛范。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留紊撕,地道東北人罢荡。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像对扶,于是被迫代替她去往敵國和親区赵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,811評論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理浪南,服務發(fā)現(xiàn)笼才,斷路器,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 手起 拍揮 球落 可能是桌上 地上 甚至天花板 想耍點花招的時候用點腦 更多的 身體 比腦快 是一種本能 看到...
    Tiramisu_U閱讀 256評論 4 1
  • 這篇文章是我在學習數(shù)據(jù)結構時作筆記的用途怨愤,這篇文章會紀錄下我學習的幾種排序算法派敷,以及在學習的時候會加上配圖說明! ...
    凌云壯志幾多愁閱讀 1,557評論 0 3
  • 今天的主題是解讀壓力與管理情緒,80后已是而立之年了赵,正處于人生的關鍵時期潜支,隨之而來的壓力也是巨大的,今天的這門課對...
    洪溢超閱讀 319評論 0 4