前言
實習過了一個多星期,原以爲可以學點新技術,結果新技術基本沒接觸到,追蹤源碼和對理解設計模式的能力倒是有所進步。接下來就記錄一下在SpringBoot中利用 @ControllerAdvice 註解實現SpringBoot的全局異常。
介紹
@ControllerAdvice 是SpringMVC中的功能,利用的是AOP的思想,也就是面向切面的思想,在SpringBoot中可以直接使用,利用這個註解可以實現以下三個功能:
- 全局異常處理
- 全局數據綁定
- 全局數據預處理
用法
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;
}
}
結果: