实习过了一个多星期,原以为可以学点新技术,结果新技术基本没接触到,追踪源码和对理解设计模式的能力倒是有所进步。接下来就记录一下在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 {
/**
* 禁止通过构造函数构造对象,只能通过静态方法获取实例。
*
* @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
* @return
*/
private 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;
}
}
结果: