日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

解決spring @ControllerAdvice處理異常無法正確匹配自定義異常

瀏覽:34日期:2023-07-09 16:46:58

首先說結(jié)論,使用@ControllerAdvice配合@ExceptionHandler處理全局controller的異常時,如果想要正確匹配自己的自定義異常,需要在controller的方法上拋出相應(yīng)的自定義異常,或者自定義異常繼承RuntimeException類。

問題描述:

1、在使用@ControllerAdvice配合@ExceptionHandler處理全局異常時,自定義了一個AppException(extends Exception),由于有些全局的參數(shù)需要統(tǒng)一驗證,所以在所有controller的方法上加一層AOP校驗,如果參數(shù)校驗沒通過也拋出AppException

2、在@ControllerAdvice標(biāo)記的類上,主要有兩個@ExceptionHandler,分別匹配AppException.class和Throwable.class。

3、在測試時,由于全局AOP的參數(shù)校驗沒通過,拋出了AppException,但是發(fā)現(xiàn)這個AppException被Throwable.class匹配到了,而不是我們想要的AppException.class匹配上。

分析過程:一階段

開始由于一直測試的兩個不同的請求(一個通過swagger,一個通過游覽器地址輸入,兩個請求比較相似,我以為是同一個請求),一個方法上拋出了AppException,一個沒有,然后發(fā)現(xiàn)這個問題時現(xiàn)時不現(xiàn),因為無法穩(wěn)定復(fù)現(xiàn)問題,我猜測可能是AppException出了問題,所以我修改了AppException,將其父類改為了RuntimeException,然后發(fā)現(xiàn)問題解決了

二階段

問題解決后,我又思考了下為啥會出現(xiàn)這種情況,根據(jù)java的異常體系來說,無論是繼承Exception還是RuntimeException,都不應(yīng)該會匹配到Throwable.class上去。

我再次跟蹤了異常的執(zhí)行過程,粗略的過了一遍,發(fā)現(xiàn)在下面這個位置出現(xiàn)了差別:

catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... Throwable targetException = ex.getTargetException(); if (targetException instanceof RuntimeException) {throw (RuntimeException) targetException; } else if (targetException instanceof Error) {throw (Error) targetException; } else if (targetException instanceof Exception) {throw (Exception) targetException; } else {String text = getInvocationErrorMessage('Failed to invoke handler method', args);throw new IllegalStateException(text, targetException); }}

成功的走的是Exception,失敗的走的是RuntimeException。

這時候到了@ControllerAdvice標(biāo)記的類時就會出問題了,因為繼承AppException是和RuntimeException是平級,所以如果走runtimeException這個判斷條件拋出去的異常注定就不會被AppException匹配上。

這時候再仔細(xì)對比下異常類型,可以發(fā)現(xiàn)正確的那個異常類型時AppException,而錯誤的那個異常類型時java.lang.reflect.UndeclaredThrowableException,內(nèi)部包著AppException。

JDK的java doc是這么解釋UndeclaredThrowableException的:如果代理實例的調(diào)用處理程序的 invoke 方法拋出一個經(jīng)過檢查的異常(不可分配給 RuntimeException 或 Error 的 Throwable),且該異常不可分配給該方法的throws子局聲明的任何異常類,則由代理實例上的方法調(diào)用拋出此異常。

因為AppException繼承于Exception,所以代理拋出的異常就是包著AppException的UndeclaredThrowableException,在@ControllerAdvice匹配的時候自然就匹配不上了。

而當(dāng)AppException繼承于RuntimeException時,拋出的異常依舊是AppException,所以能夠被匹配上。

結(jié)論:所以解決方法有兩種:AppException繼承RuntimeException或者Controller的方法拋出AppException異常。

Spring的@ExceptionHandler和@ControllerAdvice統(tǒng)一處理異常

之前敲代碼的時候,避免不了各種try…catch,如果業(yè)務(wù)復(fù)雜一點,就會發(fā)現(xiàn)全都是try…catch

try{ ..........}catch(Exception1 e){ ..........}catch(Exception2 e){ ...........}catch(Exception3 e){ ...........}

這樣其實代碼既不簡潔好看 ,我們敲著也煩, 一般我們可能想到用攔截器去處理, 但是既然現(xiàn)在Spring這么火,AOP大家也不陌生, 那么Spring一定為我們想好了這個解決辦法.果然:

@ExceptionHandler

源碼

//該注解作用對象為方法@Target({ElementType.METHOD})//在運(yùn)行時有效@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ExceptionHandler { //value()可以指定異常類 Class<? extends Throwable>[] value() default {};}

@ControllerAdvice

源碼

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented//bean對象交給spring管理生成@Componentpublic @interface ControllerAdvice { @AliasFor('basePackages') String[] value() default {}; @AliasFor('value') String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] assignableTypes() default {}; Class<? extends Annotation>[] annotations() default {};}

從名字上可以看出大體意思是控制器增強(qiáng)

所以結(jié)合上面我們可以知道,使用@ExceptionHandler,可以處理異常, 但是僅限于當(dāng)前Controller中處理異常,

@ControllerAdvice可以配置basePackage下的所有controller. 所以結(jié)合兩者使用,就可以處理全局的異常了.

一、代碼

這里需要聲明的是,這個統(tǒng)一異常處理類,也是基于ControllerAdvice,也就是控制層切面的,如果是過濾器拋出的異常,不會被捕獲!!!

在@ControllerAdvice注解下的類,里面的方法用@ExceptionHandler注解修飾的方法,會將對應(yīng)的異常交給對應(yīng)的方法處理。

@ExceptionHandler({IOException.class})public Result handleException(IOExceptione) { log.error('[handleException] ', e); return ResultUtil.failureDefaultError(); }

比如這個,就是捕獲IO異常并處理。

廢話不多說,代碼:

package com.zgd.shop.core.exception;import com.zgd.shop.core.error.ErrorCache;import com.zgd.shop.core.result.Result;import com.zgd.shop.core.result.ResultUtil;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.springframework.http.HttpStatus;import org.springframework.http.converter.HttpMessageNotReadableException;import org.springframework.validation.BindException;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.web.HttpMediaTypeNotSupportedException;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.MissingServletRequestParameterException;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;import javax.validation.ConstraintViolation;import javax.validation.ConstraintViolationException;import javax.validation.ValidationException;import java.util.Set;/** * GlobalExceptionHandle * 全局的異常處理 * * @author zgd * @date 2019/7/19 11:01 */@ControllerAdvice@ResponseBody@Slf4jpublic class GlobalExceptionHandle { /** * 請求參數(shù)錯誤 */ private final static String BASE_PARAM_ERR_CODE = 'BASE-PARAM-01'; private final static String BASE_PARAM_ERR_MSG = '參數(shù)校驗不通過'; /** * 無效的請求 */ private final static String BASE_BAD_REQUEST_ERR_CODE = 'BASE-PARAM-02'; private final static String BASE_BAD_REQUEST_ERR_MSG = '無效的請求'; /** * 頂級的異常處理 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({Exception.class}) public Result handleException(Exception e) { log.error('[handleException] ', e); return ResultUtil.failureDefaultError(); } /** * 自定義的異常處理 * * @param ex * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({BizServiceException.class}) public Result serviceExceptionHandler(BizServiceException ex) { String errorCode = ex.getErrCode(); String msg = ex.getErrMsg() == null ? '' : ex.getErrMsg(); String innerErrMsg; String outerErrMsg; if (BASE_PARAM_ERR_CODE.equalsIgnoreCase(errorCode)) { innerErrMsg = '參數(shù)校驗不通過:' + msg; outerErrMsg = BASE_PARAM_ERR_MSG; } else if (ex.isInnerError()) { innerErrMsg = ErrorCache.getInternalMsg(errorCode); outerErrMsg = ErrorCache.getMsg(errorCode); if (StringUtils.isNotBlank(msg)) {innerErrMsg = innerErrMsg + ',' + msg;outerErrMsg = outerErrMsg + ',' + msg; } } else { innerErrMsg = msg; outerErrMsg = msg; } log.info('【錯誤碼】:{},【錯誤碼內(nèi)部描述】:{},【錯誤碼外部描述】:{}', errorCode, innerErrMsg, outerErrMsg); return ResultUtil.failure(errorCode, outerErrMsg); } /** * 缺少servlet請求參數(shù)拋出的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({MissingServletRequestParameterException.class}) public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { log.warn('[handleMissingServletRequestParameterException] 參數(shù)錯誤: ' + e.getParameterName()); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } /** * 請求參數(shù)不能正確讀取解析時,拋出的異常,比如傳入和接受的參數(shù)類型不一致 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({HttpMessageNotReadableException.class}) public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { log.warn('[handleHttpMessageNotReadableException] 參數(shù)解析失敗:', e); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } /** * 請求參數(shù)無效拋出的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({MethodArgumentNotValidException.class}) public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { BindingResult result = e.getBindingResult(); String message = getBindResultMessage(result); log.warn('[handleMethodArgumentNotValidException] 參數(shù)驗證失敗:' + message); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } private String getBindResultMessage(BindingResult result) { FieldError error = result.getFieldError(); String field = error != null ? error.getField() : '空'; String code = error != null ? error.getDefaultMessage() : '空'; return String.format('%s:%s', field, code); } /** * 方法請求參數(shù)類型不匹配異常 * * @param e * @return */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({MethodArgumentTypeMismatchException.class}) public Result handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { log.warn('[handleMethodArgumentTypeMismatchException] 方法參數(shù)類型不匹配異常: ', e); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } /** * 請求參數(shù)綁定到controller請求參數(shù)時的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({BindException.class}) public Result handleHttpMessageNotReadableException(BindException e) { BindingResult result = e.getBindingResult(); String message = getBindResultMessage(result); log.warn('[handleHttpMessageNotReadableException] 參數(shù)綁定失敗:' + message); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } /** * javax.validation:validation-api 校驗參數(shù)拋出的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({ConstraintViolationException.class}) public Result handleServiceException(ConstraintViolationException e) { Set<ConstraintViolation<?>> violations = e.getConstraintViolations(); ConstraintViolation<?> violation = violations.iterator().next(); String message = violation.getMessage(); log.warn('[handleServiceException] 參數(shù)驗證失敗:' + message); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } /** * javax.validation 下校驗參數(shù)時拋出的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({ValidationException.class}) public Result handleValidationException(ValidationException e) { log.warn('[handleValidationException] 參數(shù)驗證失敗:', e); return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG); } /** * 不支持該請求方法時拋出的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ExceptionHandler({HttpRequestMethodNotSupportedException.class}) public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { log.warn('[handleHttpRequestMethodNotSupportedException] 不支持當(dāng)前請求方法: ', e); return ResultUtil.failure(BASE_BAD_REQUEST_ERR_CODE, BASE_BAD_REQUEST_ERR_MSG); } /** * 不支持當(dāng)前媒體類型拋出的異常 * * @param e * @return */ @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) @ExceptionHandler({HttpMediaTypeNotSupportedException.class}) public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { log.warn('[handleHttpMediaTypeNotSupportedException] 不支持當(dāng)前媒體類型: ', e); return ResultUtil.failure(BASE_BAD_REQUEST_ERR_CODE, BASE_BAD_REQUEST_ERR_MSG); }}

至于返回值,就可以理解為controller層方法的返回值,可以返回@ResponseBody,或者頁面。我這里是一個@ResponseBody的Result<>,前后端分離。

我們也可以自己根據(jù)需求,捕獲更多的異常類型。

包括我們自定義的異常類型。比如:

package com.zgd.shop.core.exception;import lombok.Data;/** * BizServiceException * 業(yè)務(wù)拋出的異常 * @author zgd * @date 2019/7/19 11:04 */@Datapublic class BizServiceException extends RuntimeException{ private String errCode; private String errMsg; private boolean isInnerError; public BizServiceException(){ this.isInnerError=false; } public BizServiceException(String errCode){ this.errCode =errCode; this.isInnerError = false; } public BizServiceException(String errCode,boolean isInnerError){ this.errCode =errCode; this.isInnerError = isInnerError; } public BizServiceException(String errCode,String errMsg){ this.errCode =errCode; this.errMsg = errMsg; this.isInnerError = false; } public BizServiceException(String errCode,String errMsg,boolean isInnerError){ this.errCode =errCode; this.errMsg = errMsg; this.isInnerError = isInnerError; }}

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
日本成人在线网站| 亚洲成人精品| 久久91导航| 日韩啪啪电影网| 国产中文在线播放| 成人一区而且| 在线一区av| 国产传媒av在线| 激情国产在线| 91精品国产调教在线观看| 香蕉视频亚洲一级| 2023国产精品久久久精品双| 亚洲电影在线一区二区三区| 女人天堂亚洲aⅴ在线观看| 欧美国产91| 国产日韩欧美一区| 国产图片一区| 欧美日韩1区| 国产高清日韩| 国产成人精品福利| 国产亚洲一区二区手机在线观看| 日韩久久电影| 激情欧美丁香| 丝袜诱惑制服诱惑色一区在线观看| 日韩中文字幕av电影| 亚洲免费一区三区| 亚洲精品福利| 欧美国产不卡| 久久久精品久久久久久96| 久久免费大视频| 亚洲免费中文| 视频一区二区中文字幕| 男女性色大片免费观看一区二区 | 久久精品国产免费| 日韩在线欧美| 免费久久99精品国产| 久久国产精品免费精品3p| 日本一二区不卡| 夜夜嗨一区二区三区| 日韩欧美激情| 国产一区二区三区四区五区| 久久青草久久| 蜜桃久久久久久久| 免费视频一区二区三区在线观看| 国产在线观看www| 国产精品中文字幕亚洲欧美| 91亚洲国产高清| 久久国产精品久久w女人spa| 国产伦精品一区二区三区视频| 日韩欧美在线中字| 中文字幕一区二区精品区| 国产成人免费精品| 免费美女久久99| 日本一区二区高清不卡| 亚洲欧美日韩国产| 国产一区二区三区久久| 欧美日韩日本国产亚洲在线| 国产精一区二区| 欧美 日韩 国产一区二区在线视频 | 国产一区二区精品福利地址| 欧美精品一区二区三区精品| 欧美天堂一区| 欧美日韩一二| 国产精品美女久久久久久不卡| 亚洲午夜黄色| 欧美国产不卡| 亚洲久久视频| 91看片一区| 日韩av不卡在线观看| 岛国av在线播放| 亚洲精品婷婷| 亚洲成人va| 97精品资源在线观看| 久久久精品网| 国产精品久久亚洲不卡| 久久福利影视| 日韩欧美不卡| 国产精品欧美一区二区三区不卡| 黑丝一区二区三区| 亚洲欧洲国产精品一区| 亚洲专区视频| 国产成人77亚洲精品www| 丝袜诱惑制服诱惑色一区在线观看 | 日韩成人精品一区二区| 亚洲理论在线| 1024精品一区二区三区| 久久99青青| 综合亚洲自拍| 国产二区精品| 国产黄大片在线观看| 国产精选久久| 亚洲免费毛片| 中文一区在线| 欧美性感美女一区二区| 色婷婷亚洲mv天堂mv在影片| 天堂av一区| 樱桃成人精品视频在线播放| 日本综合字幕| 成人精品视频| 国产精品成人**免费视频| 亚洲精品福利| 日韩中文字幕91| 夜夜嗨一区二区| 欧美成人国产| 色88888久久久久久影院| 精品国产成人| 国产精品久一| 精品久久美女| 亚洲欧美网站在线观看| 激情欧美日韩一区| 国内精品亚洲| 国产精品一区二区三区av麻| 日本成人在线一区| 亚洲男人在线| 中文字幕免费一区二区| 午夜在线精品偷拍| 国产综合亚洲精品一区二| 天堂av在线| 欧美男人天堂| 日韩欧美三级| 久久天堂av| 国产精品蜜芽在线观看| 日韩成人精品一区二区| 国内精品伊人| 国产精品xx| 精品美女视频 | 久久国产精品99国产| 91久久黄色| 中国女人久久久| 免费在线视频一区| 免费观看在线综合色| 只有精品亚洲| 日韩中文字幕一区二区高清99| 亚洲人成在线影院| 亚洲精品极品| 欧美亚洲一级| 麻豆一区二区在线| 国产精品一区二区三区美女| 欧美极品中文字幕| 免费在线亚洲欧美| 日韩欧美另类一区二区| 欧美高清一区| 亚洲性视频在线| 欧美亚洲三区| 加勒比视频一区| 久久国产电影| 亚洲免费黄色| 日韩成人精品一区二区三区 | 麻豆久久久久久| 日韩精品中文字幕第1页| 日韩久久一区二区三区| 国产在线成人| 亚洲深夜福利在线观看| 91大神在线观看线路一区| 麻豆一区二区三| 欧美日韩视频免费观看| 91成人超碰| 日本在线视频一区二区| 国产精品高清一区二区| 美女av在线免费看| 久久99影视| 免费观看久久av| 亚洲精品福利| 国产一区二区三区视频在线| 亚洲小说欧美另类婷婷| 国产亚洲在线| 久久国际精品| 日韩毛片视频| 久久亚洲不卡| 精品淫伦v久久水蜜桃| 国产综合色产| 久久精品xxxxx| 91精品国产乱码久久久久久久| 亚洲香蕉久久| 91综合网人人| 日韩欧美2区| 在线天堂资源www在线污| 99视频精品| 国产精品久久久久久久久久齐齐 | 日韩欧美中文字幕一区二区三区| 久久人人88| 国产精品国产三级国产在线观看| 免费视频久久| 亚洲欧美日本国产专区一区| 免费不卡中文字幕在线| 日韩激情一区| 日韩午夜在线| 欧美少妇精品| 欧美二三四区| 日韩精品亚洲一区二区三区免费| 成人免费电影网址| 亚洲精品伊人| 国产精品99一区二区三| 亚洲综合丁香| 韩日一区二区| 深夜日韩欧美| 99视频精品全国免费| 国产精品白丝一区二区三区| 黄色精品网站| 加勒比视频一区|