網站首頁 編程語言 正文
一、異常處理方式一:@ControllerAdvice+@ExceptionHandler
? ? ? ? (1)使用方法
? ? ? ? 通過@ControllerAdvice+@Exception的方式便可以指定在請求處理的整個流程中如果出現了@ExceptionHandler注解中指定的這些異常,便可以通過該@ExceptionHandler所標注的方法來處理該類異常,該類中的方法同常規的Controller中的方法一致,如果返回String便會找到相對應的頁面進行渲染,如果類上標注了@RestControllerAdvice返回類型為String則會返回JSON字符串。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String handleException() {
return "error";
}
}
? ? ? ?(2)原理分析
????????SpringMVC的核心組件是DispatcherServlet,而DispatcherServlet的核心處理流程全部在doDispatch方法中,而在這個方法的所有核心步驟中只要拋出了異常都會封裝到Exception類型的dispatchException這個變量中,并將該對象傳入到processDispatchResult方法中。該方法中會判斷是否有異常傳入,有異常傳入則會調用processHandlerException方法來獲取視圖,processHandlerException會遍歷所有實現了HandlerExceptionResolver接口類的resolveException方法看有沒有一個類可以處理該異常。
? ? ? ? 總結一下:doDispatch方法的核心流程中拋出異常→catch住異常存入dispatchException變量→調用processDispatchResult→調用processHandlerException→遍歷所有實現了HandlerExceptionResolver接口類看能否處理當前異常→能處理返回視圖進行渲染
? ? ? ? 關鍵組件:HandlerExceptionResolver接口
? ? ? ? ?上圖是在controller中拋出除0異常后遍歷handlerExceptionResolvers集合時debug觀察到的集合中的元素,其中DefaultErrorAttributes雖然實現了HandlerExceptionResolver接口但并不處理任何異常,只負責把異常對象存入request域中。而后三個resolver是在DispatcherServlet初始化時存入的,這三個resovler寫在了DispatcherServlet.properties中。
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
? ? ? ? 其中的ExceptionHandlerExceptionResolver負責在標注了@ControllerAdvice的類中找標注了@ExceptionHandler并且標注了當前異常類的方法進行處理,處理后返回視圖進行渲染。
? ? ? ? 順帶說一下ResponseStatusExceptionResolver這個resovler個人認為實際生產中用的較少就不分析了,而DefaultHandlerExceptionResolver負責處理Spring自己拋出的異常,比如下面這種異常,如果客戶端請求/demo/hello時沒有帶參數a,則會拋出org.springframework.web.bind.MissingServletRequestParameterException異常,該異常是Spring內部異常所以由DefaultHandlerExceptionResolver進行處理。
@RestController
@RequestMapping("/demo")
public class DemoController {
@RequestMapping("/hello")
public String hello(@RequestParam("a") int a) {
return "hello";
}
}
二、異常處理方式二:BasicErrorController+ErrorViewResolver
? ? ? ? (1)使用方法
????????我們使用這種處理方式處理異常其實只需要在/template/error下或者靜態資源目錄下(? “classpath:/META-INF/resources/”, “classpath:/resources/”, “classpath:/static/”, “classpath:/public/”)定義對應錯誤碼的頁面(如404.html)或者4xx.html、5xx.html頁面。
????????(2)原理分析
? ? ? ? 經過方式一不能處理的異常會通過發送/error請求再次來到DispatcherServlet的核心流程,Springboot為我們注冊了BasicErrorController來專門處理/error請求,BasicErrorController內部的處理過程則是通過ErrorViewResolver解析對應的錯誤碼并返回ModelAndView,同樣的Springboot也為我們注冊了DefaultErrorViewResolver來解析錯誤碼返回ModelAndView。
BasicErrorController處理流程:
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
//略...
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
//略...
}
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
Map<String, Object> model) {
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
DefaultErrorViewResolver解析錯誤視圖主要的流程是:
- resolveErrorView方法首先得到錯誤的http狀態碼,并根據狀態碼名稱調用resolve方法:
- resolve方法去/templates/error下面尋找名稱與狀態碼對應的模板,如404.html,如果有就會將其渲染成視圖,返回ModelAndView對象;
- 如果沒有,再調用resolveResource去靜態資源目錄下尋找:? “classpath:/META-INF/resources/”, “classpath:/resources/”, “classpath:/static/”, “classpath:/public/”
- 若仍然沒有,resolve方法最終返回ModelAndView為null;
- 當ModelAndView為null,再去驗證錯誤代碼是否是4xx或5xx,然后再按照上面resolve方法的邏輯去找是否有4xx.html或5xx.html;
- 如果仍然沒找到,返回null;
代碼如下:
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
//略...
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
//略...
}
? ? ? ? 如果我們對DefaultErrorViewResolver處理對應錯誤碼的方式不滿意,則可以通過自定義ErrorViewResolver接口的實現類加入到容器中來替換DefaultErrorViewResolver的默認行為。如果我們對BasicErrorController的處理方式不滿意,比如我們想直接返回Json字符串,則可以通過自定義ErrorController接口的實現類加入到容器中來替換BasicErrorController。
? ? ? ? 如果通過DefaultErrorViewResovler仍不能處理當前錯誤碼的/error請求,則會返回視圖名為error的ModelAndView,該視圖error最終會被BeanNameViewResolver這個視圖解析器解析得到一個bean名稱為error的View對象,調用該對象的渲染方法,最終為我們呈現出來的就是Springboot默認的錯誤白頁。??
? ? ? ? 總結:異常請求處理流程
其實方式一和方式二都屬于異常請求處理流程的一部分,方式一無法處理的異常就會由方式二進行處理。異常請求處理的流程主要分成五步:
- 解析錯誤請求拋出異常
- 嘗試處理錯誤請求(方式一處理)
- 如果處理失敗,會給底層response發送錯誤信息;然后再重新發送一個/error請求
- /error請求被ErrorController處理(方式二處理)
- 若ErrorController中的ErrorViewResolver仍不能處理返回視圖,則new一個視圖名為error的ModelAndView對象,最終該視圖會由Springboot自動裝配的BeanNameViewResolver解析得到一個bean名稱為error的View對象,該View對象進行渲染后即得到Springboot的默認錯誤白頁
????????附:Springboot自動裝配的異常處理組件
? ? ? ? 上述的許多組件都不是我們自己裝入容器的,這些組件是由Springboot為我們自動裝入的。Springboot在其自動配置包spring-boot-autoconfigure中的ErrorMvcAutoConfiguration類中主要為我們自動裝配了包括DefaultErrorAttributes、BasicErrorController、DefaultErrorViewResolver、View(bean名稱為error)、BeanNameViewResolver、
? ? ? ? 這里聲明下,本文的異常處理流程是在Springboot搭建的SSM項目環境下,如果是原生的Web.xml搭建的SSM項目則沒有Springboot裝配的這些組件,如果需要這些組件提供的功能需要自動手動進行導入到容器中。
? ? ? ? 參考博客
????????常請求處理流程 發送/error請求以及得到error視圖的詳細原理_bejsoiv的博客-CSDN博客
????????
原文鏈接:https://blog.csdn.net/koutaoran4812/article/details/125771097
相關推薦
- 2022-03-20 C++中vector容器的注意事項總結_C 語言
- 2022-12-08 C++?Boost?PropertyTree示例超詳細講解_C 語言
- 2022-11-25 Vmware臨時文件存放路徑_VMware
- 2023-11-21 python使用print輸出不同顏色的字體、終端顯示不同顏色字體
- 2022-08-16 C#在MEF框架中實現延遲加載部件_C#教程
- 2022-09-25 統一管理 Activity 便于隨時退出程序
- 2024-01-07 SpringData Jpa 之 修改、刪除數據
- 2022-08-06 ASP.NET實現Web網站本地化_實用技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支