SpringBoot 統一異常處理--工具類

前言

實習過了一個多星期,原以爲可以學點新技術,結果新技術基本沒接觸到,追蹤源碼和對理解設計模式的能力倒是有所進步。接下來就記錄一下在SpringBoot中利用 @ControllerAdvice 註解實現SpringBoot的全局異常。

介紹

@ControllerAdvice 是SpringMVC中的功能,利用的是AOP的思想,也就是面向切面的思想,在SpringBoot中可以直接使用,利用這個註解可以實現以下三個功能:

  1. 全局異常處理
  2. 全局數據綁定
  3. 全局數據預處理

用法

1. 定義常量類(個人習慣,可以不使用,只是爲了增強代碼的可維護性)

package com.chen.mykinthtest.domain;

/**
 * 對常量進行封裝,利於後期代碼的維護
 *
 * @author chen
 */
public class AppConstant {

    // 文本消息
    public static final String MESSAGE = "message";

    // 單個對象
    public static final String ITEM = "item";

    // 返回的對象列表
    public static final String LIST = "list";

    // 狀態碼
    public static final String ERROR = "error";

    // 代表執行成功
    public static int OK = 0;

    // 代表執行失敗
    public static int FAIL = 1;

    // 代表服務器運行異常
    public static int RunTime = 2;

    // 代表空指針異常
    public static int NullPointer = 3;

    // 類型轉換異常
    public static int ClassCast = 4;

    // IO異常
    public static int IO = 5;

    // 未知方法異常
    public static int NoSuchMethod = 6;

    // 數組越界異常
    public static int IndexOutOfBounds = 7;

    // 400錯誤
    public static int HttpMessageNotReadable=8;

    // 400錯誤
    public static int TypeMismatch=9;

    // 400錯誤
    public static int MissingServletRequestParameter=10;

    // 405錯誤
    public static int HttpRequestMethodNotSupported=11;

    // 406錯誤
    public static int HttpMediaTypeNotAcceptable=12;

    // 500錯誤
    public static int Run500=13;

    // 棧溢出
    public static int StackOverflow=14;

    // 除數爲0異常
    public static int Arithmetic=15;

    // 其他異常
    public static int other=16;

}

 2.封裝要返回的實體

package com.chen.mykinthtest.restful;

import com.chen.mykinthtest.domain.AppConstant;

import java.util.HashMap;
import java.util.List;

/**
 * REST 接口返回數據
 *
 * @author chen
 */
public class RestResponse extends HashMap<String, Object> {

    /**
     * 禁止通過構造函數構造對象,只能通過靜態方法獲取實例。
     *
     * @see #ok()
     * @see #ok(String)
     * @see #fail()
     * @see #fail(String)
     */
    private RestResponse() {
    }

    /**
     * 設置接口返回的文本消息,屬性 key: message
     *
     * @param msg
     * @return
     */
    public RestResponse msg(String msg) {
        this.put(AppConstant.MESSAGE, msg);
        return this;
    }

    /**
     * 設置接口返回的數據對象,屬性 key: item
     *
     * @param item
     * @return
     */
    public RestResponse item(Object item) {
        this.put(AppConstant.ITEM, item);
        return this;
    }

    /**
     * 設置接口返回的數據對象列表,屬性 key: list
     *
     * @param list
     * @return
     */
    public RestResponse list(List<?> list) {
        this.put(AppConstant.LIST, list);
        return this;
    }

    /**
     * 設置接口返回的數據項,並指定數據項的屬性 key
     *
     * @param key
     * @param value
     * @return
     */
    public RestResponse put(String key, Object value) {
        super.put(key, value);
        return this;
    }

    /**
     * 接口執行成功的返回數據,其中屬性 error = 0
     *
     * @return
     */
    public static RestResponse ok() {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, AppConstant.OK);
        return result;
    }

    /**
     * 接口執行成功的返回數據,並設置文本消息
     *
     * @param msg
     * @return
     */
    public static RestResponse ok(String msg) {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, AppConstant.OK).msg(msg);
        return result;
    }

    /**
     * 接口執行成功的返回數據,並設置對象數據
     *
     * @param item
     * @return
     */
    public static RestResponse ok(Object item) {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, AppConstant.OK).item(item);
        return result;
    }

    /**
     * 接口執行成功的返回數據,並設置列表對象數據
     *
     * @param list
     * @return
     */
    public static RestResponse ok(List<?> list) {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, AppConstant.OK).list(list);
        return result;
    }

    /**
     * 接口執行失敗的返回數據,其中屬性 error = 1
     *
     * @return
     */
    public static RestResponse fail() {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, AppConstant.FAIL);
        return result;
    }

    /**
     * 接口執行失敗的返回數據,並設置文本消息,其中屬性 error = 1, message = {msg}
     *
     * @param msg
     * @return
     */
    public static RestResponse fail(String msg) {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, AppConstant.FAIL).msg(msg);
        return result;
    }

    /**
     * 接口執行失敗的返回數據,自定義狀態碼,其中屬性 error = {errcode}
     *
     * @param errcode
     * @return
     */
    public static RestResponse fail(int errcode) {
        RestResponse result = new RestResponse();
        result.put(AppConstant.ERROR, errcode);
        return result;
    }
}

3.利用 @ControllerAdvice 結合 @ExceptionHandler 對異常進行切面

package com.chen.mykinthtest.aop;

import com.chen.mykinthtest.domain.AppConstant;
import com.chen.mykinthtest.restful.RestResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
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 java.io.IOException;

/**
 * 全局異常處理類
 *
 * @author chen
 */
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    private static final String logExceptionFormat = "服務器異常: Code: %s Detail: %s";
    private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    //運行時異常
    @ExceptionHandler(RuntimeException.class)
    public RestResponse runtimeExceptionHandler(RuntimeException ex) {
        System.err.println("RuntimeException:");
        return resultOut(AppConstant.RunTime, "服務器內部錯誤,運行異常", ex);
    }

    //空指針異常
    @ExceptionHandler(NullPointerException.class)
    public RestResponse nullPointerExceptionHandler(NullPointerException ex) {
        System.err.println("NullPointerException:");
        return resultOut(AppConstant.NullPointer, "空指針異常", ex);
    }

    //類型轉換異常
    @ExceptionHandler(ClassCastException.class)
    public RestResponse classCastExceptionHandler(ClassCastException ex) {
        System.err.println("ClassCastException:");
        return resultOut(AppConstant.ClassCast, "類型轉換異常", ex);
    }

    //IO異常
    @ExceptionHandler(IOException.class)
    public RestResponse iOExceptionHandler(IOException ex) {
        System.err.println("IOException:");
        return resultOut(AppConstant.IO, "IO異常", ex);
    }

    //未知方法異常
    @ExceptionHandler(NoSuchMethodException.class)
    public RestResponse noSuchMethodExceptionHandler(NoSuchMethodException ex) {
        System.err.println("NoSuchMethodException:");
        return resultOut(AppConstant.NoSuchMethod, "未知方法異常", ex);
    }

    //數組越界異常
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public RestResponse indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
        System.err.println("IndexOutOfBoundsException:");
        return resultOut(AppConstant.IndexOutOfBounds, "數組越界異常", ex);
    }

    //400錯誤
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public RestResponse requestNotReadable(HttpMessageNotReadableException ex) {
        System.err.println("HttpMessageNotReadableException");
        return resultOut(AppConstant.HttpMessageNotReadable, "400 bad request", ex);
    }

    //400錯誤
    @ExceptionHandler({TypeMismatchException.class})
    public RestResponse requestTypeMismatch(TypeMismatchException ex) {
        System.err.println("TypeMismatchException:");
        return resultOut(AppConstant.TypeMismatch, "400 bad request", ex);
    }

    //400錯誤
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public RestResponse requestMissingServletRequest(MissingServletRequestParameterException ex) {
        System.err.println("MissingServletRequestParameterException:");
        return resultOut(AppConstant.MissingServletRequestParameter, "400 bad request", ex);
    }

    //405錯誤
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public RestResponse request405(HttpRequestMethodNotSupportedException ex) {
        System.err.println("HttpRequestMethodNotSupportedException:");
        return resultOut(AppConstant.HttpRequestMethodNotSupported, "405 Method not allowed", ex);
    }

    //406錯誤
    @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
    public RestResponse request406(HttpMediaTypeNotAcceptableException ex) {
        System.err.println("HttpMediaTypeNotAcceptableException:");
        return resultOut(AppConstant.HttpMediaTypeNotAcceptable, "406 Not Acceptable", ex);
    }

    //500錯誤
    @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
    public RestResponse server500(RuntimeException ex) {
        System.err.println("RuntimeException:");
        return resultOut(AppConstant.HttpMediaTypeNotAcceptable, "500 error", ex);
    }

    //棧溢出
    @ExceptionHandler({StackOverflowError.class})
    public RestResponse requestStackOverflow(StackOverflowError ex) {
        System.err.println("StackOverflowError:");
        return resultOut(AppConstant.StackOverflow, "棧溢出異常", ex);
    }

    //除數不能爲0
    @ExceptionHandler({ArithmeticException.class})
    public RestResponse arithmeticException(ArithmeticException ex) {
        System.err.println("ArithmeticException:");
        return resultOut(AppConstant.Arithmetic, "除數不能爲0", ex);
    }

    //其他錯誤
    @ExceptionHandler({Exception.class})
    public RestResponse exception(Exception ex) {
        System.err.println("Exception:");
        return resultOut(AppConstant.other, "其他異常", ex);
    }

    /**
     * 對返回數據集中處理
     *
     * @param code
     * @param msg
     * @param ex
     * @param <T>
     * @return
     */
    private <T extends Throwable> RestResponse resultOut(int code, String msg, T ex) {
        ex.printStackTrace();
        log.error(String.format(logExceptionFormat, code, ex.getMessage()));
        return RestResponse.fail(code).msg(msg);
    }

}

 4.測試

package com.chen.mykinthtest.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ExceptionController {

    @RequestMapping("exceptionTest")
    public int exceptionTest() {
        return 10 / 0;
    }

}

 結果:

發佈了135 篇原創文章 · 獲贊 76 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章