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

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

 更新時(shí)間:2021年06月15日 14:39:00   作者:wkCaeser_  
這篇文章主要介紹了解決spring @ControllerAdvice處理異常無法正確匹配自定義異常的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

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

問題描述:

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

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

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

分析過程:

一階段

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

二階段

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

我再次跟蹤了異常的執(zhí)行過程,粗略的過了一遍,發(fā)現(xiàn)在下面這個(gè)位置出現(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。

這時(shí)候到了@ControllerAdvice標(biāo)記的類時(shí)就會(huì)出問題了,因?yàn)槔^承AppException是和RuntimeException是平級(jí),所以如果走runtimeException這個(gè)判斷條件拋出去的異常注定就不會(huì)被AppException匹配上。

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

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

因?yàn)锳ppException繼承于Exception,所以代理拋出的異常就是包著AppException的UndeclaredThrowableException,在@ControllerAdvice匹配的時(shí)候自然就匹配不上了。

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

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

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

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

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

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

@ExceptionHandler

源碼

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

@ControllerAdvice

源碼

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//bean對(duì)象交給spring管理生成
@Component
public @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é)合兩者使用,就可以處理全局的異常了.

一、代碼

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

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

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

比如這個(gè),就是捕獲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
@Slf4j
public class GlobalExceptionHandle {
  /**
   * 請(qǐng)求參數(shù)錯(cuò)誤
   */
  private final static String BASE_PARAM_ERR_CODE = "BASE-PARAM-01";
  private final static String BASE_PARAM_ERR_MSG = "參數(shù)校驗(yàn)不通過";
  /**
   * 無效的請(qǐng)求
   */
  private final static String BASE_BAD_REQUEST_ERR_CODE = "BASE-PARAM-02";
  private final static String BASE_BAD_REQUEST_ERR_MSG = "無效的請(qǐng)求";
  /**
   * 頂級(jí)的異常處理
   *
   * @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ù)校驗(yàn)不通過:" + 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("【錯(cuò)誤碼】:{},【錯(cuò)誤碼內(nèi)部描述】:{},【錯(cuò)誤碼外部描述】:{}", errorCode, innerErrMsg, outerErrMsg);
    return ResultUtil.failure(errorCode, outerErrMsg);
  }
  /**
   * 缺少servlet請(qǐng)求參數(shù)拋出的異常
   *
   * @param e
   * @return
   */
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ExceptionHandler({MissingServletRequestParameterException.class})
  public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
    log.warn("[handleMissingServletRequestParameterException] 參數(shù)錯(cuò)誤: " + e.getParameterName());
    return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG);
  }
  /**
   * 請(qǐng)求參數(shù)不能正確讀取解析時(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);
  }
  /**
   * 請(qǐng)求參數(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ù)驗(yàn)證失?。? + 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);
  }
  /**
   * 方法請(qǐng)求參數(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);
  }
  /**
   * 請(qǐng)求參數(shù)綁定到controller請(qǐng)求參數(shù)時(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 校驗(yàn)參數(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ù)驗(yàn)證失?。? + message);
    return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG);
  }
  /**
   * javax.validation 下校驗(yàn)參數(shù)時(shí)拋出的異常
   *
   * @param e
   * @return
   */
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ExceptionHandler({ValidationException.class})
  public Result handleValidationException(ValidationException e) {
    log.warn("[handleValidationException] 參數(shù)驗(yàn)證失?。?, e);
    return ResultUtil.failure(BASE_PARAM_ERR_CODE, BASE_PARAM_ERR_MSG);
  }
  /**
   * 不支持該請(qǐng)求方法時(shí)拋出的異常
   *
   * @param e
   * @return
   */
  @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
  @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
  public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
    log.warn("[handleHttpRequestMethodNotSupportedException] 不支持當(dāng)前請(qǐ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,或者頁(yè)面。我這里是一個(gè)@ResponseBody的Result<>,前后端分離。

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

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

package com.zgd.shop.core.exception;
import lombok.Data;
/**
 * BizServiceException
 * 業(yè)務(wù)拋出的異常
 * @author zgd
 * @date 2019/7/19 11:04
 */
@Data
public 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;
  }
}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java 中的io模型詳解

    Java 中的io模型詳解

    這篇文章主要介紹了Java 中io模型的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-04-04
  • SpringCloud全面解析@FeignClient標(biāo)識(shí)接口的過程

    SpringCloud全面解析@FeignClient標(biāo)識(shí)接口的過程

    這篇文章主要介紹了SpringCloud全面解析@FeignClient標(biāo)識(shí)接口的過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java的各種類型轉(zhuǎn)換全部匯總(推薦)

    java的各種類型轉(zhuǎn)換全部匯總(推薦)

    下面小編就為大家?guī)硪黄猨ava的各種類型轉(zhuǎn)換全部匯總(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-05-05
  • Java Spring事務(wù)的隔離級(jí)別詳解

    Java Spring事務(wù)的隔離級(jí)別詳解

    這篇文章主要介紹了Java Spring事務(wù)的隔離級(jí)別,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2021-10-10
  • springboot如何為web層添加統(tǒng)一請(qǐng)求前綴

    springboot如何為web層添加統(tǒng)一請(qǐng)求前綴

    這篇文章主要介紹了springboot如何為web層添加統(tǒng)一請(qǐng)求前綴,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • IDEA添加Java類注釋模版的方法

    IDEA添加Java類注釋模版的方法

    本篇文章主要介紹了IDEA添加Java類注釋模版的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • java實(shí)現(xiàn)文件讀寫與壓縮實(shí)例

    java實(shí)現(xiàn)文件讀寫與壓縮實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)文件讀寫與壓縮實(shí)例,有助于讀者加深對(duì)文件操作的理解,需要的朋友可以參考下
    2014-07-07
  • SpringBoot 統(tǒng)一異常處理的實(shí)現(xiàn)示例

    SpringBoot 統(tǒng)一異常處理的實(shí)現(xiàn)示例

    本文主要介紹了SpringBoot 統(tǒng)一異常處理的實(shí)現(xiàn)示例,目的就是在異常發(fā)生時(shí),盡可能地減少破壞,下面就來介紹一下,感興趣的可以了解一下
    2024-07-07
  • Java解析Excel內(nèi)容的方法

    Java解析Excel內(nèi)容的方法

    這篇文章主要介紹了Java解析Excel內(nèi)容的方法,實(shí)例分析了java解析excel文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03
  • Java中的Native關(guān)鍵字講解

    Java中的Native關(guān)鍵字講解

    本文介紹了Java中的Native關(guān)鍵字,native關(guān)鍵字是架起本機(jī)語言和JAVA之間鴻溝的橋梁。如果我們的軟件與硬件的交互在使用預(yù)先存在的代碼時(shí)更有效,那么這可以作為一個(gè)關(guān)鍵環(huán)節(jié)。與從頭開始設(shè)計(jì)新的應(yīng)用程序代碼相比,只要可以避免,它就可以使實(shí)現(xiàn)工作更少,下面來了解集體內(nèi)容
    2021-12-12

最新評(píng)論

赤城县| 汶川县| 塔河县| 顺平县| 赤壁市| 合阳县| 胶南市| 新泰市| 兖州市| 西峡县| 南投县| 鄢陵县| 新乡市| 洛浦县| 顺平县| 元氏县| 祁门县| 宝清县| 临夏市| 孟连| 景洪市| 桃江县| 石嘴山市| 南溪县| 綦江县| 新巴尔虎左旗| 张家港市| 卓资县| 乌兰浩特市| 蓬溪县| 自贡市| 舟曲县| 梧州市| 芜湖县| 治县。| 稻城县| 镇巴县| 历史| 云安县| 和政县| 海林市|