日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

SpringMVC異常處理流程總結

作者:Y4Snail 更新時間: 2022-08-15 編程語言

一、異常處理方式一:@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解析錯誤視圖主要的流程是:

  1. resolveErrorView方法首先得到錯誤的http狀態碼,并根據狀態碼名稱調用resolve方法:
    1. resolve方法去/templates/error下面尋找名稱與狀態碼對應的模板,如404.html,如果有就會將其渲染成視圖,返回ModelAndView對象;
    2. 如果沒有,再調用resolveResource去靜態資源目錄下尋找:? “classpath:/META-INF/resources/”, “classpath:/resources/”, “classpath:/static/”, “classpath:/public/”
    3. 若仍然沒有,resolve方法最終返回ModelAndView為null;
  2. 當ModelAndView為null,再去驗證錯誤代碼是否是4xx或5xx,然后再按照上面resolve方法的邏輯去找是否有4xx.html或5xx.html;
  3. 如果仍然沒找到,返回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默認的錯誤白頁。??

? ? ? ? 總結:異常請求處理流程

其實方式一和方式二都屬于異常請求處理流程的一部分,方式一無法處理的異常就會由方式二進行處理。異常請求處理的流程主要分成五步:

  1. 解析錯誤請求拋出異常
  2. 嘗試處理錯誤請求(方式一處理)
  3. 如果處理失敗,會給底層response發送錯誤信息;然后再重新發送一個/error請求
  4. /error請求被ErrorController處理(方式二處理)
  5. 若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

欄目分類
最近更新