開發與維運

SpringBoot自定義異常,優雅解決業務邏輯中的錯誤

@[toc]

前言

  • 在我們開發中,總會碰到一些異常 ---------- 運行時異常(不受檢異常):RuntimeException類極其子類表示JVM在運行期間可能出現的錯誤。編譯器不會檢查此類異常,並且不要求處理異常,比如用空值對象的引用(NullPointerException)、數組下標越界(ArrayIndexOutBoundException)。此類異常屬於不可查異常,一般是由程序邏輯錯誤引起的,在程序中可以選擇捕獲處理,也可以不處理。
  • 對於這些異常,如果返回給前端,會給用戶代碼很不好的體驗,因為用戶不知道 NullPointerException 等異常的意思,只會吐槽我們的項目,嚴重會導致用戶流失,下面我們優雅的解決程序運行中的一場
  • 設計

## 1. 自定義枚舉類

  • 使用枚舉統一管理我們的錯誤信息

  1. enum ResultEnum {

    UNKNOWN_ERROR(-100, "未知錯誤"),
    NEED_LOGIN(-1, "未登錄"),
    REPEAT_REGISTER(-2, "該用戶已註冊"),
    USER_NOT_EXIST(-3, "不存在該用戶"),
    PASSWORD_ERROR(-4, "密碼錯誤"),
    EMPTY_USERNAME(-5, "用戶名為空"),
    EMPTY_PASSWORD(-6, "密碼為空"),
    SUCCESS(0, "success"),
    SYSTEM_ERROR(500,"手速太快了,慢點");

    private Integer code;

    private String msg;

    private ResultEnum(Integer code, String msg) {

    this.code = code;
    this.msg = msg;

    }

    public Integer getCode() {

    return code;

    }

    public String getMsg() {

    return msg;

    }
    }

2. 自定義異常

package com.yxl.exception;


import com.yxl.enums.ResultEnum;

/**
 * @Author:byteblogs
 * @Date:2018/09/27 12:52
 */
public class BusinessException extends RuntimeException {

    private int code;

    private String errMsg;

    public  BusinessException(ResultEnum resultEnum){
        this.code = resultEnum.getCode();
        this.errMsg = resultEnum.getMsg();
    }

    public Integer getCode() {
        return code;
    }

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

    public String getErrMsg() {
        return errMsg;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }
}

3. 異常全局處理

  • 創建handler文件夾
  • 新增ExcepetionHandler類

    • 加入 @ControllerAdvice 註解

**加粗樣式**

  • 對於@ControllerAdvice,我們比較熟知的用法是結合@ExceptionHandler用於全局異常的處理,但其作用不僅限於此。ControllerAdvice拆分開來就是Controller
    Advice,關於Advice,前面我們講解Spring

Aop時講到,其是用於封裝一個切面所有屬性的,包括切入點和需要織入的切面邏輯。這裡ContrllerAdvice也可以這麼理解,其抽象級別應該是用於對Controller進行“切面”環繞的,而具體的業務織入方式則是通過結合其他的註解來實現的。@ControllerAdvice是在類上聲明的註解,其用法主要有三點:
結合方法型註解@ExceptionHandler,用於捕獲Controller中拋出的指定類型的異常,從而達到不同類型的異常區別處理的目的;

處理全局異常

  • ResultBody 是返回的項目統一格式
package com.yxl.handler;

import com.yxl.enums.ResultEnum;
import com.yxl.exception.BusinessException;
import com.yxl.po.ResponseMessage;
import com.yxl.po.ResultBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.stream.Collectors;

@ControllerAdvice
public class ExceptionHandler {

    private final static Logger logger= LoggerFactory.getLogger(ExceptionHandler.class);

    /**
     * 處理其他異常
     * @param e
     * @return
     */
    @org.springframework.web.bind.annotation.ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseMessage handel(Exception e){
        if(e instanceof BusinessException){
            BusinessException myException =(BusinessException)e;
            return ResultBody.error( myException.getCode(),myException.getMessage());
        }else {
            logger.error("[系統異常] {}",e);
            return ResultBody.error(ResultEnum.SYSTEM_ERROR);
        }
    }


    /**
     * 處理BusinessException異常
     * @param e
     * @return
     */
    @org.springframework.web.bind.annotation.ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public ResponseMessage busin(BusinessException e) {
        return ResultBody.error(e.getCode(), e.getErrMsg());
    }

    /**
     * 處理空指針的異常
     *
     * @param req
     * @param e
     * @return
     */
    @org.springframework.web.bind.annotation.ExceptionHandler(value = NullPointerException.class)
    @ResponseBody
    public ResponseMessage exceptionHandler(HttpServletRequest req, NullPointerException e) {
        logger.error("發生空指針異常!原因是:", e);
        return ResultBody.error(ResultEnum.SYSTEM_ERROR);
    }
    /**
     * 處理參數校驗異常
     *
     * @param req
     * @param e
     * @return
     */
    @org.springframework.web.bind.annotation.ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseMessage exceptionHandler(HttpServletRequest req, MethodArgumentNotValidException  e) {

        logger.error("參數校驗異常:", e);
        String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
        return ResultBody.error(ResultEnum.SYSTEM_ERROR);
    }

    /**
     * 處理參數校驗異常 --Json 轉換異常
     * @param req
     * @param e
     * @return
     */
    @org.springframework.web.bind.annotation.ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseBody
    public ResponseMessage exceptionHandler(HttpServletRequest req, HttpMessageNotReadableException e) {
        logger.error("參數校驗異常-json轉換異常:", e);
        return ResultBody.error(ResultEnum.SYSTEM_ERROR);
    }

}

4. 使用

  @GetMapping("/test2")
    public ResponseMessage test2(@ApiParam(value = "id")@RequestParam(required = false) Long id) {
        //如果等於空拋出異常
        if(Objects.isNull(id)){
            throw new BusinessException(ResultEnum.EMPTY_USERNAME);
        }
        return ResultBody.success();
    }
  • 結果 拋出了我們自定義的枚舉異常
    在這裡插入圖片描述

Leave a Reply

Your email address will not be published. Required fields are marked *