SpringMVC使用@ExceptionHandler注解在Controller中處理異常
異常是每一個應(yīng)用必須要處理的問題
Spring MVC項目,如果不做任何的異常處理的話,發(fā)生異常后,異常堆棧信息會直接拋出到頁面。
在Controller寫一個異常
@GetMapping(value="/hello",produces={"text/html; charset=UTF-8"}) @ResponseBody public String hello(ModelAndView model){ int c = 100 / 0; return "<h1>User info</h1>"; }
瀏覽器訪問:
用戶體驗相當(dāng)不好,所以一般情況下,應(yīng)用必須要想辦法處理異常。
異常處理方式
常見的異常處理方式,無非:
- 應(yīng)用中對所有可能發(fā)生異常的地方,都try catch,捕獲異常后做相應(yīng)的處理。
- 集中處理異常。
第一種方式顯然不好,一方面是代碼中需要到處都寫try catch,萬一某一段代碼由于程序員的疏忽沒有寫,異常就會拋出到前臺,很不好。
另外,某些情況下異常是不能捕獲的,比如需要事務(wù)處理的代碼,捕獲異常后會影響到事務(wù)回滾。
所以,我們還是需要想辦法用第2中方式來處理異常。n多年前曾經(jīng)做過一個摩托羅拉的項目,其中一項需求就是對一個線上系統(tǒng)的異常做處理、不允許異常信息拋出到前臺頁面。當(dāng)初那個項目并沒有采用類似SpringMVC的框架,所以最終提出了用filter、在filter中攔截異常的處理方案并且被客戶采納。
其實SpringMVC的統(tǒng)一異常處理方案和我上面項目中采用的方案非常類似。
SpringMVC的異常處理
Spring MVC提供了如下異常處理選項:
- @ExceptionHandle
- @ExceptionHandle + @ControllerAdvice
- 自定義異常處理器HandlerExceptionResolver
@ExceptionHandle
@ExceptionHandle可以作用在Controller中,比如,在上面發(fā)生異常的Controller中增加一個@ExceptionHandle注解的方法:
@GetMapping(value="/hello",produces={"text/html; charset=UTF-8"}) @ResponseBody public String hello(ModelAndView model){ int c = 100 / 0; return "<h1>User info</h1>"; } @ExceptionHandler(Exception.class) @ResponseBody public String handle(Exception ex){ return "錯了在helloworld Controller error msg is ==="; }
運行:
說明Hello方法中發(fā)生的異常,已經(jīng)被handle方法處理,前臺頁面不再會出現(xiàn)異常信息。
問題解決了!
但是@ExceptionHandle只在當(dāng)前Controller文件中生效,也就是說,當(dāng)前Controller中的方法、或者方法調(diào)用的service層、dao層等發(fā)生的異常,才會被捕獲到。其他Controller中發(fā)生的異常依然不會被捕獲。
這樣的話,就需要對每一個Controller增加@ExceptionHandle進行處理,處理起來還是有點麻煩。
統(tǒng)一異常處理
@ExceptionHandle + @ControllerAdvice可以實現(xiàn)統(tǒng)一異常處理。
@ControllerAdvice注解可以實現(xiàn):
On startup, RequestMappingHandlerMapping and ExceptionHandlerExceptionResolver detect controller advice beans and apply them at runtime. Global @ExceptionHandler methods, from an @ControllerAdvice, are applied after local ones, from the @Controller. By contrast, global @ModelAttribute and @InitBinder methods are applied before local ones.
SpringMVC啟動的過程中,RequestMappingHandlerMapping和ExceptionHandlerExceptionResolver會檢測到advice bean并且在運行時會使用他們。在@ControllerAdvice中的@ExceptionHandler會變成一個全局的異常處理器、在本地異常處理器之后生效。并且,@ModelAttribute和 @InitBinder會在本地的之后生效。
這段話的意思就是,@ExceptionHandle + @ControllerAdvice之后,@ExceptionHandle就會變成全局異常處理器。所謂的本地異常處理器,就是寫在Controller中的@ExceptionHandle異常處理器。
這個工作是ExceptionHandlerExceptionResolver干的,源碼中可以看到:
@Nullable protected ServletInvocableHandlerMethod getExceptionHandlerMethod( @Nullable HandlerMethod handlerMethod, Exception exception) { Class<?> handlerType = null; //先找"local"異常處理器 if (handlerMethod != null) { // Local exception handler methods on the controller class itself. // To be invoked through the proxy, even in case of an interface-based proxy. handlerType = handlerMethod.getBeanType(); ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); if (resolver == null) { resolver = new ExceptionHandlerMethodResolver(handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); } // For advice applicability check below (involving base packages, assignable types // and annotation presence), use target class instead of interface-based proxy. if (Proxy.isProxyClass(handlerType)) { handlerType = AopUtils.getTargetClass(handlerMethod.getBean()); } } //再找advice的異常處理器 for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); if (advice.isApplicableToBeanType(handlerType)) { ExceptionHandlerMethodResolver resolver = entry.getValue(); Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } return null; }
驗證一下。
加一個MyGlobalExceptionController:
@ControllerAdvice public class MyGlobalExceptionController { @ExceptionHandler(Exception.class) @ResponseBody public String handle(Exception ex){ return return "錯了MyGlobalExceptionController error msg is ==="; } }
先不去掉HelloWorldController中的異常處理器,運行hello:
生效的是HelloWorldController中的異常處理器。
然后去掉HelloWorldController中的異常處理器:
寫在MyGlobalExceptionController中的全局異常處理器生效!
自定義異常處理器HandlerExceptionResolver
實現(xiàn)接口HandlerExceptionResolver,或者擴展虛擬類AbstractHandlerMethodExceptionResolver(勉強可以考慮),定義自己的異常處理器。
其實,非必要(沒想到有什么必要性)不建議!
以上就是SpringMVC使用@ExceptionHandler注解在Controller中處理異常的詳細內(nèi)容,更多關(guān)于SpringMVC異常處理的資料請關(guān)注腳本之家其它相關(guān)文章!
- Spring中的@ExceptionHandler注解統(tǒng)一異常處理詳解
- Spring的異常處理@ExceptionHandler注解解析
- 關(guān)于SpringBoot使用@ExceptionHandler注解局部異常處理
- Spring中@ExceptionHandler注解的使用方式
- Spring中@ExceptionHandler注解的工作原理詳解
- Spring @ExceptionHandler注解統(tǒng)一異常處理和獲取方法名
- Spring中的@ControllerAdvice和@ExceptionHandler注解處理全局異常
- Spring中的@ExceptionHandler注解詳解與應(yīng)用示例
相關(guān)文章
Windows編寫jar啟動腳本和關(guān)閉腳本的操作方法
腳本文件,通常放入/bin目錄下,編寫啟動腳本需要保證能夠識別到對應(yīng)的jar文件,其次需要保證能夠識別到/config中的配置文件信息,這篇文章主要介紹了Windows編寫jar啟動腳本和關(guān)閉腳本的操作方法,需要的朋友可以參考下2022-12-12mybatis查詢實現(xiàn)返回List<Map>類型數(shù)據(jù)操作
這篇文章主要介紹了mybatis查詢實現(xiàn)返回List<Map>類型數(shù)據(jù)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11