溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

spring Boot項目怎么處理全局異常

發(fā)布時間:2021-09-09 11:48:44 來源:億速云 閱讀:189 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“spring Boot項目怎么處理全局異常”,在日常操作中,相信很多人在spring Boot項目怎么處理全局異常問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”spring Boot項目怎么處理全局異常”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

前言

異常的處理在我們的日常開發(fā)中是一個繞不過去的坎,在Spring Boot 項目中如何優(yōu)雅的去處理異常,正是我們這一節(jié)課需要研究的方向。

異常的分類

在一個Spring Boot項目中,我們可以把異常分為兩種,第一種是請求到達Controller層之前,第二種是到達Controller層之后項目代碼中發(fā)生的錯誤。而第一種又可以分為兩種錯誤類型:1. 路徑錯誤 2. 類似于請求方式錯誤,參數(shù)類型不對等類似錯誤。

spring Boot項目怎么處理全局異常

定義ReturnVO和ReturnCode

為了保持返回值的統(tǒng)一,我們這里定義了統(tǒng)一返回的類ReturnVO,以及一個記錄錯誤返回碼和錯誤信息的枚舉類ReturnCode,而具體的錯誤信息和錯誤代碼保存到了response.properties中,使用流進行讀取。

ReturnVO
public class ReturnVO {

    private static Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + CommonUrl.RESPONSE_PROP_URL);

    /**
     * 返回代碼
     */
    private String code;

    /**
     * 返回信息
     */
    private String message;

    /**
     * 返回數(shù)據(jù)
     */
    private Object data;


    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    /**
     * 默認構(gòu)造,返回操作正確的返回代碼和信息
     */
    public ReturnVO() {
        this.setCode(properties.getProperty(ReturnCode.SUCCESS.val()));
        this.setMessage(properties.getProperty(ReturnCode.SUCCESS.msg()));
    }

    /**
     * 返回代碼,這里需要在枚舉中去定義
     * @param code
     */
    public ReturnVO(ReturnCode code) {
        this.setCode(properties.getProperty(code.val()));
        this.setMessage(properties.getProperty(code.msg()));
    }

    /**
     * 返回數(shù)據(jù),默認返回正確的code和message
     * @param data
     */
    public ReturnVO(Object data) {
        this.setCode(properties.getProperty(ReturnCode.SUCCESS.val()));
        this.setMessage(properties.getProperty(ReturnCode.SUCCESS.msg()));
        this.setData(data);
    }

    /**
     * 返回錯誤的代碼,以及自定義的錯誤信息
     * @param code
     * @param message
     */
    public ReturnVO(ReturnCode code, String message) {
        this.setCode(properties.getProperty(code.val()));
        this.setMessage(message);
    }

    /**
     * 返回自定義的code,message,以及data
     * @param code
     * @param message
     * @param data
     */
    public ReturnVO(ReturnCode code, String message, Object data) {
        this.setCode(code.val());
        this.setMessage(message);
        this.setData(data);
    }

    @Override
    public String toString() {
        return "ReturnVO{" +
                "code='" + code + '\'' +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}
ReturnCode

其他的錯誤處理只需要在枚舉類中添加對應(yīng)的異常即可,枚舉的名稱要定義為異常的名稱,這樣可以直接不用對其他的代碼進行修改,添加一個新的異常時,僅僅添加枚舉類中的字段和properties文件中的屬性。

public enum ReturnCode {

    /** 操作成功 */
    SUCCESS("SUCCESS_CODE", "SUCCESS_MSG"),

    /** 操作失敗 */
    FAIL("FAIL_CODE", "FAIL_MSG"),

    /** 空指針異常 */
    NullPointerException("NPE_CODE", "NPE_MSG"),

    /** 自定義異常之返回值為空 */
    NullResponseException("NRE_CODE", "NRE_MSG"),

    /** 運行時異常 */
    RuntimeException("RTE_CODE","RTE_MSG"),

    /** 請求方式錯誤異常 */
    HttpRequestMethodNotSupportedException("REQUEST_METHOD_UNSUPPORTED_CODE","REQUEST_METHOD_UNSUPPORTED_MSG"),

    /** INTERNAL_ERROR */
    BindException("BIND_EXCEPTION_CODE","BIND_EXCEPTION_MSG"),

    /** 頁面路徑不對 */
    UrlError("UE_CODE","UE_MSG");

    private ReturnCode(String value, String msg){
        this.val = value;
        this.msg = msg;
    }

    public String val() {
        return val;
    }

    public String msg() {
        return msg;
    }

    private String val;
    private String msg;
}
response.properties

這里我自定義了一些異常用于后面的測試,在我們實際的項目中需要定義很多的異常去完善。

SUCCESS_CODE=2000
SUCCESS_MSG=操作成功

FAIL_CODE=5000
FAIL_MSG=操作失敗

NPE_CODE=5001
NPE_MSG=空指針異常

NRE_CODE=5002
NRE_MSG=返回值為空

RTE_CODE=5001
RTE_MSG=運行時異常

UE_CODE=404
UE_MSG=頁面路徑有誤

REQUEST_METHOD_UNSUPPORTED_CODE=4000
REQUEST_METHOD_UNSUPPORTED_MSG=請求方式異常

BIND_EXCEPTION_CODE=4001
BIND_EXCEPTION_MSG=請求參數(shù)綁定失敗

路徑錯誤處理

這里的路徑錯誤處理方式是采用了實現(xiàn)ErrorController接口,然后實現(xiàn)了getErrorPath()方法:

/**
 * 請求路徑有誤
 * @author yangwei
 * @since 2019-01-02 18:13
 */
@RestController
public class RequestExceptionHandler implements ErrorController {

    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping("/error")
    public ReturnVO errorPage(){
        return new ReturnVO(ReturnCode.UrlError);
    }
}

這里可以進行測試一下:

使用ControllerAdvice對其他類型的異常進行處理

類似于到達Controller之前的請求參數(shù)錯誤,請求方式錯誤,數(shù)據(jù)格式不對等等錯誤都歸類為一種,這里僅僅展示請求方式錯誤的處理方式。

/**
 * 全局異常處理類
 * @author yangwei
 *
 * 用于全局返回json,如需返回ModelAndView請使用ControllerAdvice
 * 繼承了ResponseEntityExceptionHandler,對于一些類似于請求方式異常的異常進行捕獲
 */
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    private static Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + CommonUrl.RESPONSE_PROP_URL);

    /**
     * 重寫handleExceptionInternal,自定義處理過程
     **/
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        //這里將異常直接傳給handlerException()方法進行處理,返回值為OK保證友好的返回,而不是出現(xiàn)500錯誤碼。
        return new ResponseEntity<>(handlerException(ex), HttpStatus.OK);
    }

    /**
     * 異常捕獲
     * @param e 捕獲的異常
     * @return 封裝的返回對象
     **/
    @ExceptionHandler(Exception.class)
    public ReturnVO handlerException(Throwable e) {
        ReturnVO returnVO = new ReturnVO();
        String errorName = e.getClass().getName();
        errorName = errorName.substring(errorName.lastIndexOf(".") + 1);
        //如果沒有定義異常,而是直接拋出一個運行時異常,需要進入以下分支
        if (e.getClass() == RuntimeException.class) {
            returnVO.setMessage(properties.getProperty(valueOf("RuntimeException").msg()) +": "+ e.getMessage());
            returnVO.setCode(properties.getProperty(valueOf("RuntimeException").val()));
        } else {
            returnVO.setMessage(properties.getProperty(valueOf(errorName).msg()));
            returnVO.setCode(properties.getProperty(valueOf(errorName).val()));
        }
        return returnVO;
    }
}

這里我們可以進行測試:

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @PostMapping(value = "/findAll")
    public Object findAll() {
        throw new RuntimeException("ddd");
    }

    @RequestMapping(value = "/findAll1")
    public ReturnVO findAll1(UserDO userDO) {
        System.out.println(userDO);
        return new ReturnVO(userService.findAll1());
    }

   @RequestMapping(value = "/test")
    public ReturnVO test() {
        throw new RuntimeException("測試非自定義運行時異常");
    }
}

直接在瀏覽器訪問findAll,默認為get方法,這里按照我們期望會拋出請求方式異常的錯誤:

spring Boot項目怎么處理全局異常

訪問findAll1?id=123ss,這里由于我們接受的UserDOid屬性是Integer類型,所以這里報一個參數(shù)綁定異常:

spring Boot項目怎么處理全局異常

結(jié)合AOP使用,放入公用模塊減少代碼的重復

我們上節(jié)課使用AOP對于全局異常處理進行了一次簡單的操作,這節(jié)課進行了完善,并將其放入到我們的公用模塊,使用時只需導入jar包,然后在啟動類配置掃描包路徑即可

/**
 * 統(tǒng)一封裝返回值和異常處理
 *
 * @author vi
 * @since 2018/12/20 6:09 AM
 */
@Slf4j
@Aspect
@Order(5)
@Component
public class ResponseAop {

    @Autowired
    private GlobalExceptionHandler exceptionHandler;

    /**
     * 切點
     */
    @Pointcut("execution(public * indi.viyoung.viboot.*.controller..*(..))")
    public void httpResponse() {
    }

    /**
     * 環(huán)切
     */
    @Around("httpResponse()")
    public ReturnVO handlerController(ProceedingJoinPoint proceedingJoinPoint) {
        ReturnVO returnVO = new ReturnVO();
        try {
            Object proceed = proceedingJoinPoint.proceed();
            if (proceed instanceof ReturnVO) {
                returnVO = (ReturnVO) proceed;
            } else {
                returnVO.setData(proceed);
            }
        }  catch (Throwable throwable) {
            // 這里直接調(diào)用剛剛我們在handler中編寫的方法
            returnVO =  exceptionHandler.handlerException(throwable);
        }
        return returnVO;
    }
}

做完這些準備工作,以后我們在進行異常處理的時候只需要進行以下幾步操作:

  • 引入公用模塊jar包

  • 在啟動類上配置掃描包路徑

  • 如果新增異常的話,在枚舉類中新增后,再去properties中進行返回代碼和返回信息的編輯即可(注意:枚舉類的變量名一定要和異常名保持一致

到此,關(guān)于“spring Boot項目怎么處理全局異?!钡膶W習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI