如何在SpringBoot中優(yōu)雅的處理異常
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)
真香!24W字的Java面試手冊(cè)(點(diǎn)擊查看)
SpringBoot 統(tǒng)一異常處理 像這種統(tǒng)一異常的文章博客有許多,但是每個(gè)人使用都有自己的心得,我來(lái)總結(jié)一下自己使用的心得統(tǒng)一異常,顧名思義,就是統(tǒng)一管理項(xiàng)目中會(huì)方法的異常,然后進(jìn)行一個(gè)處理,Spring發(fā)生錯(cuò)誤后,底層會(huì)去請(qǐng)求一個(gè)/error的地址,拋出對(duì)應(yīng)的異常到頁(yè)面上,對(duì)客戶或者開發(fā)來(lái)說(shuō)都不是特別的友好使用統(tǒng)一異常處理的話,可以返回自定義的異常數(shù)據(jù),閱讀性提高,優(yōu)雅的處理異常
使用異常
使用異常的方式很簡(jiǎn)單,Spring提供了兩個(gè)注解:@ControllerAdvice和@ExceptionHandler
@ControllerAdvice 控制器增強(qiáng),使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法應(yīng)用到所有的 @RequestMapping注解的方法 @ExceptionHandler 異常處理器,此注解的作用是當(dāng)出現(xiàn)其定義的異常時(shí)進(jìn)行處理的方法
創(chuàng)建統(tǒng)一異常類
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.Map;
/**
* 全局異常處理
* 一般情況下,方法都有異常處理機(jī)制,但不能排除有個(gè)別異常沒(méi)有處理,導(dǎo)致返回到前臺(tái),因此在這里做一個(gè)異常攔截,統(tǒng)一處理那些未被處理過(guò)的異常
*
* @author ${author} on ${date}
*/
@ControllerAdvice
@Controller
@RequestMapping
public class GlobalExceptionHandler extends AbstractErrorController {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
public GlobalExceptionHandler(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Value("${server.error.path:${error.path:/error}}")
private static String errorPath = "/error";
/**
* sql異常
*
* @param req
* @param rsp
* @param ex
* @return
* @throws Exception
*/
@ResponseBody
@ExceptionHandler(SQLException.class)
public Result<String> sqlException(HttpServletRequest req, HttpServletResponse rsp, Exception ex) {
LOGGER.error("!!! request uri:{} from {} server exception:{}", req.getRequestURI(), RequestUtil.getIpAddress(req), ex == null ? null : ex);
return ResponseMsgUtil.builderResponse(1002, ex == null ? null : ex.getMessage(), null);
}
/**
* 500錯(cuò)誤.
*
* @param req
* @param rsp
* @param ex
* @return
* @throws Exception
*/
@ResponseBody
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Result<String> serverError(HttpServletRequest req, HttpServletResponse rsp, Exception ex) throws Exception {
LOGGER.error("!!! request uri:{} from {} server exception:{}", req.getRequestURI(), RequestUtil.getIpAddress(req), ex == null ? null : ex);
return ResponseMsgUtil.builderResponse(1002, ex == null ? null : ex.getMessage(), null);
}
/**
* 404的攔截.
*
* @param request
* @param response
* @param ex
* @return
* @throws Exception
*/
@ResponseBody
@ResponseStatus(code = HttpStatus.NOT_FOUND)
@ExceptionHandler(NoHandlerFoundException.class)
public Result<String> notFound(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
LOGGER.error("!!! request uri:{} from {} not found exception:{}", request.getRequestURI(), RequestUtil.getIpAddress(request), ex);
return ResponseMsgUtil.builderResponse(404, ex == null ? null : ex.getMessage(), null);
}
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public Result<String> paramException(MissingServletRequestParameterException ex) {
LOGGER.error("缺少請(qǐng)求參數(shù):{}", ex.getMessage());
return ResponseMsgUtil.builderResponse(99999, "缺少參數(shù):" + ex.getParameterName(), null);
}
//參數(shù)類型不匹配
//getPropertyName()獲取數(shù)據(jù)類型不匹配參數(shù)名稱
//getRequiredType()實(shí)際要求客戶端傳遞的數(shù)據(jù)類型
@ExceptionHandler(TypeMismatchException.class)
@ResponseBody
public Result<String> requestTypeMismatch(TypeMismatchException ex) {
LOGGER.error("參數(shù)類型有誤:{}", ex.getMessage());
return ResponseMsgUtil.builderResponse(99999, "參數(shù)類型不匹配,參數(shù)" + ex.getPropertyName() + "類型應(yīng)該為" + ex.getRequiredType(), null);
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public Result<String> requestMethod(HttpRequestMethodNotSupportedException ex) {
LOGGER.error("請(qǐng)求方式有誤:{}", ex.getMethod());
return ResponseMsgUtil.builderResponse(99999, "請(qǐng)求方式有誤:" + ex.getMethod(), null);
}
@ExceptionHandler(MultipartException.class)
@ResponseBody
public Result<String> fileSizeLimit(MultipartException m) {
LOGGER.error("超過(guò)文件上傳大小限制");
return ResponseMsgUtil.builderResponse(99999, "超過(guò)文件大小限制,最大10MB", null);
}
/**
* 重寫/error請(qǐng)求, ${server.error.path:${error.path:/error}} IDEA報(bào)紅無(wú)需處理,作用是獲取spring底層錯(cuò)誤攔截
*
* @param request
* @param response
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "${server.error.path:${error.path:/error}}")
public Result<String> handleErrors(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NOT_FOUND) {
throw new NoHandlerFoundException(request.getMethod(), request.getRequestURL().toString(), new HttpHeaders());
}
Map<String, Object> body = getErrorAttributes(request, true);
return ResponseMsgUtil.builderResponse(Integer.parseInt(body.get("status").toString()), body.get("message").toString(), null);
}
@Override
public String getErrorPath() {
return errorPath;
}
}
從上面可以看出來(lái),我定義了sql異常,500異常,404異常該做的事情,通過(guò)@ExceptionHandler注解來(lái)攔截程序中的異常,比如執(zhí)行SQL時(shí),拋出了異常,就會(huì)被統(tǒng)一異常給攔截,然后返回我們想要返回的數(shù)據(jù)
@ResponseStatus注解可加可不加,就是對(duì)響應(yīng)碼進(jìn)行攔截,如代碼上,對(duì)404響應(yīng)碼進(jìn)行了攔截
最下面的handleErrors方法,是對(duì)Spring底層訪問(wèn)/error的時(shí)候進(jìn)行了一次攔截,獲取當(dāng)前請(qǐng)求碼,如果是404.拋出404的異常
優(yōu)化處理異常怎么能沒(méi)有自定義返回?cái)?shù)據(jù)呢
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* 返回?cái)?shù)據(jù)結(jié)果集合
*/
public class Result<T> {
private Integer code;
private String resMsg;
private T data;
public Result() {
}
public Integer getCode() {
return this.code;
}
public void setCode(Integer resCode) {
this.code = resCode;
}
public String getResMsg() {
return this.resMsg;
}
public void setResMsg(String resMsg) {
this.resMsg = resMsg;
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
public String toJson() {
return this.data == null ? JSON.toJSONString(this) : this.toJson(SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue);
}
public String toJson(SerializerFeature... features) {
return features == null ? this.toJson() : JSON.toJSONString(this, features);
}
public String toString() {
return "Result{code=" + this.code + ", resMsg='" + this.resMsg + '\'' + ", data=" + this.data + '}';
}
}
/**
* 返回?cái)?shù)據(jù)結(jié)果集合
* @author RuXuanWo on 2019/02/21
*/
public class ResponseMsgUtil {
public ResponseMsgUtil() {
}
public static <T> Result<T> builderResponse(int code, String msg, T data) {
Result<T> res = new Result();
res.setCode(code);
res.setResMsg(msg);
res.setData(data);
return res;
}
public static <T> Result<T> success(String msg) {
return builderResponse(0, msg, null);
}
public static <T> Result<T> success(String msg, T data) {
return builderResponse(0, msg, data);
}
public static <T> Result<T> success(T data) {
return builderResponse(0, "Success", data);
}
public static <T> Result<T> success() {
return builderResponse(0, "Success", null);
}
public static <T> Result<T> failure() {
return builderResponse(1, "Failure", null);
}
public static <T> Result<T> failure(String msg) {
return builderResponse(1, msg, null);
}
public static <T> Result<T> failure(T date) {
return builderResponse(-1, "Failure", date);
}
public static <T> Result<T> illegalRequest() {
return builderResponse(1008, "Illegal request", (T) null);
}
public static <T> Result<T> exception() {
return builderResponse(1002, "request exception", (T) null);
}
public static <T> Result<T> paramsEmpty() {
return builderResponse(1009, "the input parameter is null", (T) null);
}
}
測(cè)試
將這些準(zhǔn)備都做好以后,項(xiàng)目跑起來(lái),訪問(wèn)一個(gè)接口,故意不傳某個(gè)必填項(xiàng),就會(huì)被統(tǒng)一異常攔截,如下
{
code: 1002,
data: null,
msg: "Required String parameter 'id' is not present"
}
如有文章對(duì)你有幫助,
歡迎關(guān)注??、點(diǎn)贊??、轉(zhuǎn)發(fā)??!
推薦, Java面試手冊(cè) 內(nèi)容包括網(wǎng)絡(luò)協(xié)議、Java基礎(chǔ)、進(jìn)階、字符串、集合、并發(fā)、JVM、數(shù)據(jù)結(jié)構(gòu)、算法、MySQL、Redis、Mongo、Spring、SpringBoot、MyBatis、SpringCloud、Linux以及各種中間件(Dubbo、Nginx、Zookeeper、MQ、Kafka、ElasticSearch)等等... 點(diǎn)擊文末“閱讀原文”可直達(dá)

