天天看點

SpringBoot之全局異常處理

異常處理問題分析

異常如何處理

問題引入

  • 針對代碼中的異常,正常有兩種處理方式,一種throws直接抛出,另一種try…catch捕獲。
  • 在java項目中,有可能存在人為邏輯的異常,也可能為取得異常的詳情,或是保證程式在異常時繼續向下執行,會采用第二種處理方式。
  • 但是,代碼中每一處異常都來捕獲,會使代碼什麼備援且不利于維護。

解決思路

  • 定義一個全局異常處理類,傳回統一規範的異常資訊;
  • 處理邏輯是,先判定是否會出現異常,再執行後續具體的業務。

業務舉例

本文主要為了實作全局異常處理的邏輯,隻舉簡單業務

某公司部門需增加員工,處理流程:1先根據員工編号查詢員工對象,2判斷員工對象是否有資訊,即是否不為空,3若有資訊,則說明已存在,無需再添加,若不是,則直接添加。

代碼如下:

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);
    }
}
           

異常處理流程

業務中存在運作時異常和業務邏輯異常,前者不運作時很難察覺,後者在遍及業務時就可以定義出來,是以異常分為不可預知異常和可知異常。流程如下:

  1. 自定義全局異常類,使用

    @ControllerAdvice

    ,控制器增強
  2. 自定義錯誤代碼及錯誤資訊,兩種異常最終會采用統一的資訊格式來表示,錯誤代碼+錯誤資訊。
  3. 對于可預知的異常由程式員在代碼中主動抛出,由

    SpringMVC

    統一捕獲。
  4. 不可預知異常通常是由于系統出現bug、或一些外界因素(如網絡波動、伺服器當機等),異常類型為

    RuntimeException

    類型(運作時異常)。

可知異常

定義異常資訊類,變量為錯誤代碼和錯誤資訊,捕獲自定義異常時,直接将該對象傳回

不可知異常

定義一個map,将常見的異常存入其中,并定義錯誤代碼。對于其他不常見的異常,即map中沒有的,同一一個異常對象傳回即可。

異常處理代碼流程

可知異常

1、定義列印異常資訊與傳回結果的接口

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;
}
           

2、定義列印異常資訊的枚舉類和傳回結果類

@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();
    }
}
           

3、定義錯誤異常類

public class CustomException extends RuntimeException{

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

4、定義異常抛出類

public class ExceptionCast {

    // 靜态方法
    public static void cast(ResultCode resultCode){
        throw new CustomException(resultCode);
    }
}
           

5、定義異常捕獲類,使用

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);
    }
}
           

6、在業務中抛出異常

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);
    }
}
           

不可知異常處理

1、類似可知異常,先添加錯誤代碼,如

2、在異常捕獲類中添加不可知異常的捕獲方法。該方法中,定義一個隻讀的map存儲異常類型的錯誤代碼的映射,map中沒有的元素,同一用錯誤代碼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);
    }
}
           

完成~~

繼續閱讀