SpringBoot之全局異常處理

異常處理問題分析

異常如何處理

問題引入

  • 針對代碼中的異常,常規有兩種處理方式,一種throws直接拋出,另外一種try..catch捕獲。java

  • 在java項目中,有可能存在人爲邏輯的異常,也可能爲取得異常的詳情,或是保證程序在異常時繼續向下執行,會採用第二種處理方式。程序員

  • 可是,代碼中每一處異常都來捕獲,會使代碼什麼冗餘且不利於維護。json

解決思路

  • 定義一個全局異常處理類,返回統一規範的異常信息;服務器

  • 處理邏輯是,先斷定是否會出現異常,再執行後續具體的業務。網絡

業務舉例

本文主要爲了實現全局異常處理的邏輯,只舉簡單業務app

某公司部門需增長員工,處理流程:1先根據員工編號查詢員工對象,2判斷員工對象是否有信息,便是否不爲空,3如有信息,則說明已存在,無需再添加,若不是,則直接添加。ide

代碼以下:ui

public class MyService {
    // 注入dao層
    @Autowired
    EmployeeecMapper employeeecMapper;

    /** * 添加員工信息 * @param employee 員工對象 * @return 影響的行數 */
    public int add(Employee employee) {
        // 根據id查詢員工對象
        Employeeec emp = employeeecMapper.selectByPrimaryKey(employee.getId());
        // 判斷是否已有該員工
        if (emp != null){
            // 已有,拋出異常,異常信息爲已有該員工
            throw new RuntimeException("異常代碼:1201,錯誤信息:該員工已存在");
        }
        // 沒有,插入該員工
        return employeeecMapper.insert(emp);
    }
}
複製代碼

異常處理流程

業務中存在運行時異常和業務邏輯異常,前者不運行時很難察覺,後者在遍佈業務時就能夠定義出來,所以異常分爲不可預知異常和可知異常。流程以下:this

  1. 自定義全局異常類,使用@ControllerAdvice,控制器加強
  2. 自定義錯誤代碼及錯誤信息,兩種異常最終會採用統一的信息格式來表示,錯誤代碼+錯誤信息。
  3. 對於可預知的異常由程序員在代碼中主動拋出,由SpringMVC統一捕獲。
  4. 不可預知異常一般是因爲系統出現bug、或一些外界因素(如網絡波動、服務器宕機等),異常類型爲RuntimeException類型(運行時異常)。

可知異常

定義異常信息類,變量爲錯誤代碼和錯誤信息,捕獲自定義異常時,直接將該對象返回spa

不可知異常

定義一個map,將常見的異常存入其中,並定義錯誤代碼。對於其餘不常見的異常,即map中沒有的,同一一個異常對象返回便可。

異常處理代碼流程

可知異常

一、定義打印異常信息與返回結果的接口

public interface ResultCode {
    // 操做是否成功
    boolean success();

    // 操做結果代碼
    long code();

    // 提示信息
    String message();
}
複製代碼
public interface Response {
    public static final boolean SUCCESS = true;
    public static final int SUCCESS_CODE = 10000;
}
複製代碼

二、定義打印異常信息的枚舉類和返回結果類

@ToString
public enum  CommonCode implements ResultCode {
    NO_PAGE(false,404,"沒有信息"),
    FAIL(false,500,"操做失敗!"),
    SUCCESS(true,200,"操做成功!");

    // 結果信息
    boolean success;
    long code;
    String message;
    
    // 帶參構造
    CommonCode(boolean success, long code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }

    @Override
    public boolean success() {
        return true;
    }

    @Override
    public long code() {
        return code;
    }

    @Override
    public String message() {
        return message;
    }
}
複製代碼
@Data
@ToString
public class ResponseResult implements Response {

    boolean success = SUCCESS;

    long code = SUCCESS_CODE;

    String message;

    public ResponseResult(ResultCode resultCode){
        this.success = resultCode.success();
        this.code = resultCode.code();
        this.message = resultCode.message();
    }
}
複製代碼

三、定義錯誤異常類

public class CustomException extends RuntimeException{

    @Autowired
    ResultCode resultCode;
    
    // 帶參構造
    public CustomException(ResultCode resultCode){
        this.resultCode = resultCode;
    }
    
    // getter
    public ResultCode getResultCode(){
        return resultCode;
    }
}
複製代碼

四、定義異常拋出類

public class ExceptionCast {

    // 靜態方法
    public static void cast(ResultCode resultCode){
        throw new CustomException(resultCode);
    }
}
複製代碼

五、定義異常捕獲類,使用ControllerAdvice控制器加強的註解,並在捕獲CustomException異常的方法上加ExceptionHandler註解,便可捕獲該類的全部異常,返回json數據。

@ControllerAdvice 
public class ExceptionCatch {

    /** * 捕獲CustomException類異常 * @param customException * @return 結果信息,json數據 */
    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public ResponseResult customException(CustomException customException){
        ResultCode resultCode = customException.getResultCode();
        return new ResponseResult(resultCode);
    }
}
複製代碼

六、在業務中拋出異常

public class MyService {

    @Autowired
    EmployeeecMapper employeeecMapper;

    public int add(Employee employee) {
        Employeeec emp = employeeecMapper.selectByPrimaryKey(employee.getId());
        if (emp != null){
            ExceptionCast.cast(CommonCode.FAIL);
        }
        return employeeecMapper.insert(emp);
    }
}
複製代碼

不可知異常處理

一、相似可知異常,先在CommonCode類中添加錯誤代碼,如

UNAUTHORISE(false,510,"沒有權限"),
複製代碼

二、在異常捕獲類中添加不可知異常的捕獲方法。該方法中,定義一個只讀的map存儲異常類型的錯誤代碼的映射,map中沒有的元素,統一用錯誤代碼999來定義。

UNKNOWNERROR(false,999,"未知異常"),
複製代碼
@ControllerAdvice
public class ExceptionCatch {

    // 定義map,存貯常見錯誤信息。該類map不可修改
    private static ImmutableMap<Class<? extends Throwable>,ResultCode> EXCEPTIONS;
    // 構建ImmutableMap
    protected static ImmutableMap.Builder<Class<? extends Throwable>,ResultCode> builder = ImmutableMap.builder();

    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public ResponseResult customException(CustomException customException){
        ResultCode resultCode = customException.getResultCode();
        return new ResponseResult(resultCode);
    }

    /** * 捕獲非自定義類異常 * @param exception * @return */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseResult exception(Exception exception){
        // 記錄日誌
        LOGGER.error("catch exception ==> ",exception.getMessage());
        if (EXCEPTIONS == null){
            EXCEPTIONS = builder.build();
        }
        ResultCode resultCode = EXCEPTIONS.get(exception.getClass());
        if (resultCode != null){
            return new ResponseResult(resultCode);
        }else {
            return new ResponseResult(CommonCode.UNKNOWNERROR);
        }
    }

    static {
        builder.put(HttpMessageNotReadableException.class, CommonCode.INVALID_PARAM);
    }
}
複製代碼

完成~~

相關文章
相關標籤/搜索