免费在线a视频-免费在线观看a视频-免费在线观看大片影视大全-免费在线观看的视频-色播丁香-色播基地

面試官:你能給我談談Spring MVC的異常處理機制嗎?

:2020年04月02日 Java面試那些事兒
分享到:

# 前言SpringMVC是目前主流的Web MVC框架之一。 本文將分析SpringMVC的異常處理內容,讓讀者了解SpringMVC異常處理的設計原理。# 重要接口和類介紹1. HandlerExceptionResolver接口SpringMVC異...

# 前言

SpringMVC是目前主流的Web MVC框架之一。 

本文將分析SpringMVC的異常處理內容,讓讀者了解SpringMVC異常處理的設計原理。

# 重要接口和類介紹

1. HandlerExceptionResolver接口

SpringMVC異常處理核心接口。該接口定義了1個解析異常的方法:

ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;

2. AbstractHandlerExceptionResolver抽象類

實現了HandlerExceptionResolver和Ordered接口的抽象類。

先看下屬性:

再看下接口的實現:

3. AbstractHandlerMethodExceptionResolver抽象類

繼承AbstractHandlerExceptionResolver抽象類的抽象類。該類主要就是為HandlerMethod類服務,既handler參數是HandlerMethod類型。

該類重寫了shouldApplyTo方法:

doResolveException抽象方法的實現中調用了doResolveHandlerMethodException方法,該方法也是1個抽象方法。

4. ExceptionHandlerExceptionResolver類

繼承自AbstractHandlerMethodExceptionResolver,該類主要處理Controller中用@ExceptionHandler注解定義的方法。

該類也是<annotation-driven/>配置中定義的HandlerExceptionResolver實現類之一,大多數異常處理都是由該類操作。

該類比較重要,我們來詳細講解一下。

首先我們看下這個類的屬性:

再來看下該類的doResolveHandlerMethodException抽象方法的實現:

默認的HandlerMethodArgumentResolver集合:

默認的HandlerMethodReturnValueHandler集合:

我們進入getExceptionHandlerMethod方法看看是如何得到ServletInvocableHandlerMethod的:

我們看到getExceptionHandlerMethod中會實例化ExceptionHandlerMethodResolver,我們看看這個類到底是什么東西?

ExceptionHandlerMethodResolver是一個會在Class及Class的父類集合中找出帶有@ExceptionHandler注解的類,該類帶有key為Throwable,value為Method的緩存屬性。

ExceptionHandlerMethodResolver的構造過程:

ExceptionHandlerExceptionResolver處理過程總結一下:根據用戶調用Controller中相應的方法得到HandlerMethod,之后構造ExceptionHandlerMethodResolver,構造ExceptionHandlerMethodResolver有2種選擇,1.通過HandlerMethod拿到Controller,找出Controller中帶有@ExceptionHandler注解的方法(局部) 2.找到@ControllerAdvice注解配置的類中的@ExceptionHandler注解的方法(全局)。這2種方式構造的ExceptionHandlerMethodResolver中都有1個key為Throwable,value為Method的緩存。之后通過發生的異常找出對應的Method,然后調用這個方法進行處理。這里異常還有個優先級的問題,比如發生的是NullPointerException,但是聲明的異常有Throwable和Exception,這時候ExceptionHandlerMethodResolver找Method的時候會根據異常的最近繼承關系找到繼承深度最淺的那個異常,即Exception。

5. DefaultHandlerExceptionResolver類

繼承自AbstractHandlerExceptionResolver抽象類。<annotation-driven/>配置中定義的HandlerExceptionResolver實現類之一。

該類的doResolveException方法中主要對一些特殊的異常進行處理,比如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等。

6. ResponseStatusExceptionResolver類

繼承自AbstractHandlerExceptionResolver抽象類。<annotation-driven/>配置中定義的HandlerExceptionResolver實現類之一。

該類的doResolveException方法主要在異常及異常父類中找到@ResponseStatus注解,然后使用這個注解的屬性進行處理。

說明一下為什么ExceptionHandlerExceptionResolver、DefaultHandlerExceptionResolver、ResponseStatusExceptionResolver是<annotation-driven/>配置中定義的HandlerExceptionResolver實現類。

我們看下<annotation-driven/>配置解析類AnnotationDrivenBeanDefinitionParser中部分代碼片段:

這3個ExceptionResolver最終被會加入到DispatcherServlet中的handlerExceptionResolvers集合中。

其中ExceptionHandlerExceptionResolver優先級最高,ResponseStatusExceptionResolver第二,DefaultHandlerExceptionResolver第三。

為什么ExceptionHandlerExceptionResolver優先級最高,因為order屬性值最低

7. @ResponseStatus注解

讓1個方法或異常有狀態碼(status)和理由(reason)返回。這個狀態碼是http響應的狀態碼。

8. SimpleMappingExceptionResolver類

繼承自AbstractHandlerExceptionResolver抽象類,是1個根據配置進行解析異常的類,包括配置異常類型,默認的錯誤視圖,默認的響應碼,異常映射等配置屬性。本文不分析,有興趣的讀者可自行查看源碼。

# 源碼分析

下面我們來分析SpringMVC處理異常的源碼。

SpringMVC在處理請求的時候,通過RequestMappingHandlerMapping得到HandlerExecutionChain,然后通過RequestMappingHandlerAdapter得到1個ModelAndView對象,這在之前發生的異常都會被catch到,然后得到這個異常并作為參數傳入到processDispatchResult方法處理。

processDispatchResult方法如下:

processHandlerException方法:

# 實例講解

接下里講常用ExceptionResolver的實例。

1. ExceptionHandlerExceptionResolver

@Controller

@RequestMapping(value = "/error")

public class TestErrorController {

@RequestMapping("/exception")

public ModelAndView exception(ModelAndView view) throws ClassNotFoundException {

view.setViewName("index");

throw new ClassNotFoundException("class not found");

}

@RequestMapping("/nullpointer")

public ModelAndView nullpointer(ModelAndView view) {

view.setViewName("index");

String str = null;

str.length();

return view;

}

@ExceptionHandler(RuntimeException.class)

public ModelAndView error(RuntimeException error, HttpServletRequest request) {

ModelAndView mav = new ModelAndView();

mav.setViewName("error");

mav.addObject("param", "Runtime error");

return mav;

}

@ExceptionHandler()

public ModelAndView error(Exception error, HttpServletRequest request, HttpServletResponse response) {

ModelAndView mav = new ModelAndView();

mav.setViewName("error");

mav.addObject("param", "Exception error");

return mav;

}

/**

@ExceptionHandler(NullPointerException.class)

public ModelAndView error(ModelAndView mav) {

mav.setViewName("error");

    mav.addObject("param", "NullPointer error");

return mav;

}*/

}

分析一下:

如果用戶進入nullpointer方法,str對象還未初始化,會發生NullPointerException。如果去掉最后1個注釋掉的error方法,那么會報錯。因為ExceptionHandlerExceptionResolver的默認HandlerMethodArgumentResolver中只有ServletRequestMethodArgumentResolver和ServletResponseMethodArgumentResolver(所以其他2個error方法中的request和response參數沒問題)。所以我們給最后1個error方法加了注釋。

由于TestErrorController控制器中有2個帶有@ExceptionHandler注解的方法,之前分析的ExceptionHandlerMethodResolver構造過程中,會構造ExceptionHandlerMethodResolver,ExceptionHandlerMethodResolver內部會有1個key分別為RuntimeException和Exception,value分別為第一個和第二個error方法的緩存。由于NullPointerException的繼承關系離RuntimeException比Exception近,因此最終進入了第一個error方法。

如果用戶進入exception方法,同理。ClassNotFoundException繼承自Exception,跟RuntimeException沒關系,那么進入第二個error方法。

說明一下,兩個error方法返回值都是ModelAndView,這是因為ExceptionHandlerMethodResolver的默認HandlerMethodReturnValueHandler中有ModelAndViewMethodReturnValueHandler。還有其他的比如ModelMethodProcessor、ViewMethodReturnValueHandler和ViewNameMethodReturnValueHandler等。這3個分別代表返回值Model,View和字符串。有興趣的讀者可自行查看源碼。 

上個例子是基于Controller的@ExceptionHandler注解方法,每個Controller都需要寫@ExceptionHandler注解方法(寫個BaseController可不用每個Controller都寫單獨的@ExceptionHandler注解方法)。

ExceptionHandlerMethodResolver內部找不到Controller的@ExceptionHandler注解的話,會找@ControllerAdvice中的@ExceptionHandler注解方法。

因此,我們也可以寫1個ControllerAdvice。

@ControllerAdvice

public class ExceptionControllerAdvice {

@ExceptionHandler(Throwable.class)

@ResponseBody

public Map<String, Object> ajaxError(Throwable error, HttpServletRequest request, HttpServletResponse response) {

Map<String, Object> map = new HashMap<String, Object>();

map.put("error", error.getMessage());

map.put("result", "error");

return map;

}

}

此類中的error方法代表著全局異常處理方法。

該方法可對ajax操作進行異常處理,我們返回值使用了@ResponseBody進行了處理,然后配置json消息轉換器即可,這樣該方法響應給客戶端的數據就變成了json數據。

2. ResponseStatusExceptionResolver

先定義1個自定義異常:

@ResponseStatus(HttpStatus.UNAUTHORIZED)

public class UnauthorizedException extends RuntimeException {

}

Controller代碼:

@Controller

@RequestMapping(value = "/error")

public class TestErrorController {

@RequestMapping("/unauth")

public ModelAndView unauth(ModelAndView view) {

view.setViewName("index");

throw new UnauthorizedException();

}

}

由于該類沒有寫@ExceptionHandler注解,因此ExceptionHandlerExceptionResolver不能解析unauth觸發的異常。接下來由ResponseStatusExceptionResolver進行解析,由于觸發的異常UnauthorizedException帶有@ResponseStatus注解。因此會被ResponseStatusExceptionResolver解析到。最后響應HttpStatus.UNAUTHORIZED代碼給客戶端。HttpStatus.UNAUTHORIZED代表響應碼401,無權限。關于其他的響應碼請參考HttpStatus枚舉類型源碼。

3. DefaultHandlerExceptionResolver

直接上代碼:

@Controller

@RequestMapping(value = "/error")

public class TestErrorController {

@RequestMapping("/noHandleMethod")

public ModelAndView noHandleMethod(ModelAndView view, HttpServletRequest request) throws NoSuchRequestHandlingMethodException {

view.setViewName("index");

throw new NoSuchRequestHandlingMethodException(request);

}

}

用戶進入noHandleMethod方法觸發NoSuchRequestHandlingMethodException異常,由于沒配置@ExceptionHandler以及該異常沒有@ResponseStatus注解,最終由DefaultHandlerExceptionResolver解析,由于NoSuchRequestHandlingMethodException屬于DefaultHandlerExceptionResolver解析的異常,因此被DefaultHandlerExceptionResolver解析。NoSuchRequestHandlingMethodException會發生404錯誤。

關于DefaultHandlerExceptionResolver可以處理的其他異常,請參考DefaultHandlerExceptionResolver源碼。

# 擴展ExceptionHandlerExceptionResolver功能

SpringMVC提供的HandlerExceptionResolver基本上都能滿足我們的開發要求,因此本文就不準備寫自定義的HandlerExceptionResolver。

既然不寫自定義的HandlerExceptionResolver,我們就來擴展ExceptionHandlerExceptionResolver來吧,讓它支持更多的功能。

比如為ExceptionHandlerExceptionResolver添加更多的HandlerMethodArgumentResolver,ExceptionHandlerExceptionResolver默認只能有2個HandlerMethodArgumentResolver和ServletRequestMethodArgumentResolver(處理ServletRequest、WebRequest、MultipartRequest、HttpSession等參數)和ServletResponseMethodArgumentResolver(處理ServletResponse、OutputStream或Writer參數)。

ModelAndView這種類型的參數會被ServletModelAttributeMethodProcessor處理。因此我們需要給ExceptionHandlerExceptionResolver添加ServletModelAttributeMethodProcessor這個HandlerMethodArgumentResolver。由于ServletModelAttributeMethodProcessor處理ModelAndView參數會使用WebDataBinderFactory參數,因此我們得重寫doResolveHandlerMethodException方法,所以新寫了1個類繼承ExceptionHandlerExceptionResolver。

public class MyExceptionHandlerExceptionResolver extends ExceptionHandlerExceptionResolver{

public MyExceptionHandlerExceptionResolver() {

List<HandlerMethodArgumentResolver> list = new ArrayList<HandlerMethodArgumentResolver>();

list.add(new ServletModelAttributeMethodProcessor(true));

this.setCustomArgumentResolvers(list);

}

@Override

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,

HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {

ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);

if (exceptionHandlerMethod == null) {

return null;

}

//ServletModelAttributeMethodProcessor 內部會使用傳遞進來的WebDataBinderFactory參數,該參數由ServletInvocableHandlerMethod提供

exceptionHandlerMethod.setDataBinderFactory(new ServletRequestDataBinderFactory(null, null));

exceptionHandlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers());

exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(getReturnValueHandlers());

ServletWebRequest webRequest = new ServletWebRequest(request, response);

ModelAndViewContainer mavContainer = new ModelAndViewContainer();

try {

if (logger.isDebugEnabled()) {

logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);

}

exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);

}

catch (Exception invocationEx) {

logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);

return null;

}

if (mavContainer.isRequestHandled()) {

return new ModelAndView();

}

else {

ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());

mav.setViewName(mavContainer.getViewName());

if (!mavContainer.isViewReference()) {

mav.setView((View) mavContainer.getView());

}

return mav;

}

}

}

配置:

<bean class="org.format.demo.support.exceptionResolver.MyExceptionHandlerExceptionResolver">

<property name="order" value="-1"/>

</bean>

配置完成之后,然后去掉本文實例講解中ExceptionHandlerExceptionResolver的代碼,并去掉支持NullPointerException異常的那個方法的注釋。

測試如下:

讀者可根據需求自己實現其他的擴展功能?;蛘邔崿FHandlerExceptionResolver接口新寫1個HandlerExceptionResolver實現類。

新的的HandlerExceptionResolver實現類只需在配置文件中定義即可,然后配置優先級。DispatcherServlet初始化HandlerExceptionResolver的時候會自動尋找容器中實現了HandlerExceptionResolver接口的類,然后添加進來。

# 總結

分析了SpringMVC的異常處理機制并介紹了幾個重要的接口和類,并分析了在<annotation-driven/>中定義的3個常用的HandlerExceptionResolver。

之后又編寫了1個繼承自ExceptionHandlerExceptionResolver類的異常解析類,鞏固了之前分析的知識。

希望這篇文章能幫助讀者了解SpringMVC異常機制。

文中難免有錯誤,希望讀者能夠指明出來。

# 參考資料

  • http://docs.spring.io/spring/docs/4.0.5.RELEASE/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers

[我要糾錯]
文:王振袢&發表于江蘇
關鍵詞: 前言 SpringMVC 目前 流的 框架

來源:本文內容搜集或轉自各大網絡平臺,并已注明來源、出處,如果轉載侵犯您的版權或非授權發布,請聯系小編,我們會及時審核處理。
聲明:江蘇教育黃頁對文中觀點保持中立,對所包含內容的準確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點負責,僅作分享之用,文章版權及插圖屬于原作者。

點個贊
0
踩一腳
0

您在閱讀:面試官:你能給我談談Spring MVC的異常處理機制嗎?

Copyright?2013-2024 JSedu114 All Rights Reserved. 江蘇教育信息綜合發布查詢平臺保留所有權利

蘇公網安備32010402000125 蘇ICP備14051488號-3技術支持:南京博盛藍睿網絡科技有限公司

南京思必達教育科技有限公司版權所有   百度統計

主站蜘蛛池模板: 你懂的免费在线观看 | 午夜黄色福利 | 久久er精品热线免费 | 日韩精品视频美在线精品视频 | 久久亚洲视频 | 亚洲一区二区在线免费观看 | 18jzjzz国产| 色原网 | 日韩欧美成人乱码一在线 | 五月激情久久 | 日本三级a| 日韩欧美精品综合久久 | 91久久天天躁狠狠躁夜夜 | 亚洲五月激情网 | 亚洲人交性视频 | 高清欧美一级在线观看 | 久久午夜剧场 | 黄色网址免费看 | 波多野结衣久久精品免费播放 | 波多野结衣视频免费在线观看 | 日日干日日操 | 中文字幕在线视频观看 | 国产视频一区二区在线观看 | 免费看三级全黄 | 成人美女隐私免费 | yjizz视频国产网站在线播放 | 成人中文字幕在线高清 | 成人影院在线观看免费 | 激情性爽三级成人 | 免费黄色毛片 | 精品日韩| 在线亚洲精品防屏蔽 | 中文字幕天天躁日日躁狠狠躁免费 | 精品午夜寂寞影院在线观看 | 日韩高清网站 | 免费看大美女大黄大色 | 啪啪网址大全 | 日日日操操操 | 九九视频在线观看 | 不卡国产 | 精品国产拍拍拍无遮挡 |
最熱文章
最新文章
  • 阿里云上云鉅惠,云產品享最低成本,有需要聯系,
  • 卡爾蔡司鏡片優惠店,鏡片價格低
  • 蘋果原裝手機殼