最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解SpringMVC中的異常處理機制

 更新時間:2022年07月28日 08:22:24   作者:默念x  
本篇文章將為大家詳細介紹一下springmvc的異常處理機制,用到了ControllerAdvice和ExceptionHandler注解,感興趣的小伙伴可以了解一下

開頭

試想一下我們一般怎么統(tǒng)一處理異常呢,答:切面。但拋開切面不講,如果對每一個controller方法拋出的異常做專門處理,那么著實太費勁了,有沒有更好的方法呢?當然有,就是本篇文章接下來要介紹的springmvc的異常處理機制,用到了ControllerAdvice和ExceptionHandler注解,有點切面的感覺哈哈。

1.ExceptionHandlerExceptionResolver

首先從springmvc的異常處理解析器開始講,當執(zhí)行完controller方法后,不管有沒有異常產生都會調用DispatcherServlet#doDispatch()方法中的processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 方法,接著會判斷是否有異常,若無異常則走正常流程,若有異常則需要進行處理 mv = processHandlerException(request, response, handler, exception);  再接著就是遍歷spring已經注冊的異常處理解析器直到有處理器返回mav

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				// 執(zhí)行處理器產生的異常處理
				mv = processHandlerException(request, response, handler, exception);
				// 是否有異常視圖返回
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render? 處理程序是否返回要渲染的視圖
		if (mv != null && !mv.wasCleared()) {
			// 渲染視圖
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}
	}
	@Nullable
	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {

		// Check registered HandlerExceptionResolvers...
		ModelAndView exMv = null;
		if (this.handlerExceptionResolvers != null) {
			for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
				exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
				if (exMv != null) {
					break;
				}
			}
		}
		if (exMv != null) {
			// 無視圖view
			if (exMv.isEmpty()) {
				request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
				return null;
			}
			// We might still need view name translation for a plain error model...
			if (!exMv.hasView()) {
				String defaultViewName = getDefaultViewName(request);
				if (defaultViewName != null) {
					exMv.setViewName(defaultViewName);
				}
			}
			WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
			return exMv;
		}

		throw ex;
	}

其中最重要也是最常使用的一個處理器就是ExceptionHandlerExceptionResolver,下面將著重介紹它,先來看看這個類的繼承結構圖,實現了InitializingBean接口,在這個bean創(chuàng)建完成之前會調用生命周期初始化方法afterPropertiesSet(),這里面包含了對@ControllerAdvice注解的解析,初始化完后的信息供后續(xù)解析異常使用。

實現HandlerExceptionResolver接口,實現解析方法resolveException()

public interface HandlerExceptionResolver {

	/**
	 * Try to resolve the given exception that got thrown during handler execution,
	 * returning a {@link ModelAndView} that represents a specific error page if appropriate.
	 * <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}
	 * to indicate that the exception has been resolved successfully but that no view
	 * should be rendered, for instance by setting a status code.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler, or {@code null} if none chosen at the
	 * time of the exception (for example, if multipart resolution failed)
	 * @param ex the exception that got thrown during handler execution
	 * @return a corresponding {@code ModelAndView} to forward to,
	 * or {@code null} for default processing in the resolution chain
	 */
	@Nullable
	ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}
@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    // 初始化異常注解 @ControllerAdvice
    initExceptionHandlerAdviceCache();
}

private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for exception mappings: " + getApplicationContext());
    }

    // 解析有@ControllerAdvice注解的bean,并將這個bean構建成ControllerAdviceBean對象
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    // 將ControllerAdviceBean根據order排序
    AnnotationAwareOrderComparator.sort(adviceBeans);

    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
        // mappedMethods 映射不為空
        if (resolver.hasExceptionMappings()) {
            // 添加到緩存中
            this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
            if (logger.isInfoEnabled()) {
                logger.info("Detected @ExceptionHandler methods in " + adviceBean);
            }
        }
        // 若實現了ResponseBodyAdvice接口(暫不介紹)
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
            if (logger.isInfoEnabled()) {
                logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
            }
        }
    }
}

 ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType); 這行代碼會解析擁有@ControllerAdvice 注解的class,并且會遍歷class中帶有 @ExceptionHandler注解的方法,獲取方法注解帶有的異常類型,將異常類型和方法放入到mappedMethods中供后面獲取,獲取的時候若對應處理此異常類型的method有多個,則需要進行排序,選取一個異常類型與method ExceptionHandler注解異常類型最近的一個(深度最小的那個也即是繼承關系最少的那個)具體代碼如下:

ExceptionHandlerMethodResolver

@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
                                                       HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {

    // exception為controller方法拋出的異常
    // 根據異常及其類型從上述的mappedMethods中獲取對應的方法,再獲取方法所在的對象 封裝成ServletInvocableHandlerMethod
    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
        return null;
    }

    // 設置參數解析器,主要用來獲取方法的參數值的,供后續(xù)反射調用方法
    if (this.argumentResolvers != null) {
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    // 設置返回值解析器,當執(zhí)行完方法后獲取返回值,對返回值進行處理 或返回視圖或將結果寫入到response
    if (this.returnValueHandlers != null) {
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();

    try {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
        }
        Throwable cause = exception.getCause();
        if (cause != null) {
            // Expose cause as provided argument as well
            // 執(zhí)行異常處理方法,也就是我們的自定義的異常處理方法
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
        }
        else {
            // Otherwise, just the given exception as-is
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
        }
    }
    catch (Throwable invocationEx) {
        // Any other than the original exception is unintended here,
        // probably an accident (e.g. failed assertion or the like).
        if (invocationEx != exception && logger.isWarnEnabled()) {
            logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
        }
        // Continue with default processing of the original exception...
        return null;
    }

    // 根據后續(xù)的返回值解析器設置的,將返回值寫入到response中了直接返回空的mav
    if (mavContainer.isRequestHandled()) {
        return new ModelAndView();
    }
    else {
        ModelMap model = mavContainer.getModel();
        HttpStatus status = mavContainer.getStatus();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
        mav.setViewName(mavContainer.getViewName());
        // (this.view instanceof String)
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
        return mav;
    }
}

exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod); 此方法執(zhí)行完成后已經完成了異常處理方法的調用,若方法返回值為視圖ModelAndView或其他視圖類型,則還需要借助視圖解析器如InternalResourceViewResolver對視圖進行解析渲染,若為其他類型的值則將值寫入到response響應中。

2. demo

Controller類方法:

@Controller
@RequestMapping(value = "test")
public class HelloWorldController{

  @Data
  public static class User {
    private String username;

    private Integer age;

    private String address;
  }


  @RequestMapping(value = "user/get", method = RequestMethod.POST)
  @ResponseBody
  public Object testObject(@RequestBody @Valid User user, @RequestParam String address) {
    user.setAddress(address);
    // 這里特意拋出RuntimeException異常
    throw new RuntimeException("this is a exception");
  }

}

ExceptionHandlerController異常處理類

@ControllerAdvice
@ResponseBody
public class ExceptionHandlerController {

  @ExceptionHandler(value = Exception.class)
  public Object handleException(Exception e) {
    return CommonResult.fail("Exception:" + e.getMessage());
  }

  @ExceptionHandler(value = RuntimeException.class)
  public Object handlerRuntimeException(Exception e) {
    return CommonResult.fail("handlerRuntimeException:" + e.getMessage());
  }
}

ExceptionHandlerController類中定義了兩個異常處理方法,一個處理Exception異常,一個處理RuntimeException異常,那個根據controller方法拋出的異常RuntimeException再結合上面的分析(RuntimeException到RuntimeException深度為0,RuntimeException到Exception中間繼承了一次深度為1)可以得出拋出異常類型的處理方法為handlerRuntimeException 方法。 運行程序結果如下:

到此這篇關于詳解SpringMVC中的異常處理機制的文章就介紹到這了,更多相關SpringMVC異常處理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 解決Spring boot 嵌入的tomcat不啟動問題

    解決Spring boot 嵌入的tomcat不啟動問題

    這篇文章主要介紹了解決Spring boot 嵌入的tomcat不啟動問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Spring MVC的項目準備和連接建立方法

    Spring MVC的項目準備和連接建立方法

    SpringWebMVC是基于Servlet API的Web框架,屬于Spring框架的一部分,主要用于簡化Web應用程序的開發(fā),SpringMVC通過控制器接收請求,使用模型處理數據,并通過視圖展示結果,感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • Java編程通過匹配合并數據實例解析(數據預處理)

    Java編程通過匹配合并數據實例解析(數據預處理)

    這篇文章主要介紹了Java編程通過匹配合并數據實例解析(數據預處理),分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • 基于Java手寫一個好用的FTP操作工具類

    基于Java手寫一個好用的FTP操作工具類

    網上百度了很多FTP的java?工具類,發(fā)現文章代碼都比較久遠,且代碼臃腫,即使搜到了代碼寫的還可以的,封裝的常用操作方法不全面。所以本文將手寫一個好用的Java?FTP操作工具類,需要的可以參考一下
    2022-04-04
  • java環(huán)境配好后jar文件打開命令框閃退(無打開方式,無反應)解決辦法

    java環(huán)境配好后jar文件打開命令框閃退(無打開方式,無反應)解決辦法

    在Java開發(fā)中我們經常會遇到運行Jar包時閃退的問題,下面這篇文章主要給大家介紹了關于java環(huán)境配好后jar文件打開命令框閃退(無打開方式,無反應)的解決辦法,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2024-04-04
  • Java正則表達式詳解及實用案例(附詳細代碼)

    Java正則表達式詳解及實用案例(附詳細代碼)

    正則表達式是一種強大的字符串處理工具,用于匹配和解析文本,這篇文章主要給大家介紹了關于Java正則表達式詳解及實用案例的相關資料,本文通過代碼示例講解了正則表達式的基本語法規(guī)則,包括字符類、預定義字符類和數量詞,需要的朋友可以參考下
    2024-11-11
  • 使用Springboot注入帶參數的構造函數實例

    使用Springboot注入帶參數的構造函數實例

    這篇文章主要介紹了使用Springboot注入帶參數的構造函數實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • Java將GeoHash轉化為對應的經緯度坐標實例代碼

    Java將GeoHash轉化為對應的經緯度坐標實例代碼

    這篇文章主要介紹了Java實現將GeoHash轉化為對應的經緯度坐標的相關資料,需要的朋友可以參考下
    2016-01-01
  • Java使用設計模式中的代理模式構建項目的實例展示

    Java使用設計模式中的代理模式構建項目的實例展示

    這篇文章主要介紹了Java使用設計模式中的代理模式構建項目的實例展示,代理模式中的代理對象可以在客戶端和目標對象之間起到中介的作用,需要的朋友可以參考下
    2016-05-05
  • JAVA中while循環(huán)的使用與注意事項

    JAVA中while循環(huán)的使用與注意事項

    這篇文章主要介紹了while循環(huán)在編程中的應用,包括其基本結構、語句示例、適用場景以及注意事項,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-01-01

最新評論

高碑店市| 闻喜县| 金溪县| 莲花县| 德令哈市| 新竹市| 庐江县| 海宁市| 讷河市| 绥江县| 赫章县| 湘潭市| 右玉县| 镇平县| 洮南市| 扶余县| 密山市| 武定县| 前郭尔| 凤城市| 龙山县| 济阳县| 新野县| 合肥市| 漯河市| 新邵县| 武宁县| 临武县| 松溪县| 肇东市| 绥滨县| 东乡族自治县| 商城县| 江达县| 云南省| 枞阳县| 喜德县| 铜梁县| 阿巴嘎旗| 安乡县| 保德县|