SpringBoot —— 統(tǒng)一異常處理

前言

在 Controller 里提供接口,通常需要捕捉異常心赶,進(jìn)行異常處理缨叫。最簡單的方法使用try/catch進(jìn)行異常捕捉荔燎。

當(dāng)方法很多,每個(gè)都需要 try catch琐簇,代碼會(huì)顯得臃腫座享,寫起來也比較麻煩征讲。
這時(shí)就需要進(jìn)行統(tǒng)一的異常處理。

1.使用方法

通過 Spring 的 AOP 特性就可以很方便的實(shí)現(xiàn)異常的統(tǒng)一處理:使用@ControllerAdvice癣籽、@RestControllerAdvice捕獲運(yùn)行時(shí)異常滤祖。

代碼結(jié)構(gòu)


image.png

新建異常枚舉類

package com.local.dev.root.devroot.common.enums;

/**
 * 異常枚舉類
 */
public enum ExceptionEnum {
    // 400
    BAD_REQUEST("400", "請(qǐng)求數(shù)據(jù)格式不正確!"),
    UNAUTHORIZED("401", "登錄憑證過期!"),
    FORBIDDEN("403", "沒有訪問權(quán)限!"),
    NOT_FOUND("404", "請(qǐng)求的資源找不到!"),
    // 500
    INTERNAL_SERVER_ERROR("500", "服務(wù)器內(nèi)部錯(cuò)誤!"),
    SERVICE_UNAVAILABLE("503", "服務(wù)器正忙匠童,請(qǐng)稍后再試!"),
    // 未知異常
    UNKNOWN("10000", "未知異常!"),
    // 自定義
    IS_NOT_NULL("10001","%s不能為空");

    /**
     * 錯(cuò)誤碼
     */
    private String code;

    /**
     * 錯(cuò)誤描述
     */
    private String msg;

    ExceptionEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

自定義異常類

package com.local.dev.root.devroot.common.exception;

import com.local.dev.root.devroot.common.enums.ExceptionEnum;

/**
 * 自定義業(yè)務(wù)異常類
 */
public class BusinessException extends RuntimeException {

    private ExceptionEnum exceptionEnum;
    private String code;
    private String errorMsg;

    public BusinessException() {
        super();
    }

    public BusinessException(ExceptionEnum exceptionEnum) {
        super("{code:" + exceptionEnum.getCode() + ",errorMsg:" + exceptionEnum.getMsg() + "}");
        this.exceptionEnum = exceptionEnum;
        this.code = exceptionEnum.getCode();
        this.errorMsg = exceptionEnum.getMsg();
    }

    public BusinessException(String code, String errorMsg) {
        super("{code:" + code + ",errorMsg:" + errorMsg + "}");
        this.code = code;
        this.errorMsg = errorMsg;
    }

    public BusinessException(String code, String errorMsg, Object... args) {
        super("{code:" + code + ",errorMsg:" + String.format(errorMsg, args) + "}");
        this.code = code;
        this.errorMsg = String.format(errorMsg, args);
    }

    public ExceptionEnum getExceptionEnum() {
        return exceptionEnum;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

ExceptionHandlerConfig.java
@RestControllerAdvice俏险,統(tǒng)一異常處理

package com.local.dev.root.devroot.common.config.exception;

import com.local.dev.root.devroot.common.enums.ExceptionEnum;
import com.local.dev.root.devroot.common.exception.BusinessException;
import com.local.dev.root.devroot.common.exception.ErrorPageException;
import com.local.dev.root.devroot.common.pojo.ApiResponse;
import com.local.dev.root.devroot.common.util.ErrorUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * RestControllerAdvice竖独,統(tǒng)一異常處理
 */
@Slf4j
@RestControllerAdvice
public class ExceptionHandlerConfig {

    /**
     * 業(yè)務(wù)異常處理
     *
     * @param e 業(yè)務(wù)異常
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public ApiResponse exceptionHandler(BusinessException e) {
        log.error(ErrorUtil.errorInfoToString(e));
        return ApiResponse.error(e.getCode(), e.getErrorMsg());
    }

    /**
     * 未知異常處理
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ApiResponse exceptionHandler(Exception e) {
        // 把錯(cuò)誤信息輸入到日志中
        log.error(ErrorUtil.errorInfoToString(e));
        return ApiResponse.error(ExceptionEnum.UNKNOWN.getCode(),
                ExceptionEnum.UNKNOWN.getMsg());
    }

    /**
     * 錯(cuò)誤頁面異常
     */
    @ExceptionHandler(value = ErrorPageException.class)
    @ResponseBody
    public ApiResponse exceptionHandler(ErrorPageException e) {
        log.error(ErrorUtil.errorInfoToString(e));
        return ApiResponse.error(e.getCode(), e.getErrorMsg());
    }

    /**
     * 空指針異常
     */
    @ExceptionHandler(value = NullPointerException.class)
    @ResponseBody
    public ApiResponse exceptionHandler(NullPointerException e) {
        log.error(ErrorUtil.errorInfoToString(e));
        return ApiResponse.error(ExceptionEnum.INTERNAL_SERVER_ERROR.getCode(),
                ExceptionEnum.INTERNAL_SERVER_ERROR.getMsg());
    }
}

測試類

package com.local.dev.root.devroot.controller;

import com.local.dev.root.devroot.common.pojo.ApiResponse;
import com.local.dev.root.devroot.service.dev.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloWorld {

    @Autowired
    private TestServiceImpl testServiceImpl;
    @RequestMapping("world")
    public String HelloWorld() {
        return "Hello World!";
    }

    @RequestMapping("error1")
    public ApiResponse Error1() {
        return ApiResponse.ok();
    }
    @RequestMapping("error2")
    public ApiResponse Error2() {
        String msg = null;
        msg.equals("xx");
        return ApiResponse.ok("訪問成功");
    }
    @RequestMapping("error3")
    public ApiResponse Error3() {
        testServiceImpl.getBusinessException();
        return ApiResponse.ok("訪問成功");
    }
}

testServiceImpl 拋出自定義異常

package com.local.dev.root.devroot.service.dev;

import com.local.dev.root.devroot.common.enums.ExceptionEnum;
import com.local.dev.root.devroot.common.exception.BusinessException;
import org.springframework.stereotype.Service;

@Service
public class TestServiceImpl {

    public Object getBusinessException() {
        throw new BusinessException(ExceptionEnum.IS_NOT_NULL.getCode(),
                ExceptionEnum.IS_NOT_NULL.getMsg(), "參數(shù)");
    }
}

ErrorUtil工具類

package com.local.springboot.springbootcommon.utils;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 捕獲報(bào)錯(cuò)日志處理工具類
 */
public class ErrorUtil {

    /**
     * Exception出錯(cuò)的棧信息轉(zhuǎn)成字符串
     * 用于打印到日志中
     */
    public static String errorInfoToString(Throwable e) {
        //try-with-resource語法糖 處理機(jī)制
        try(StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)){
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
            return sw.toString();
        }catch (Exception ignored){
            throw new RuntimeException(ignored.getMessage(),ignored);
        }
    }
}

ApiResponse

package com.local.springboot.springbootcommon.reponse;

import java.util.HashMap;

/**
 * @author: MaoDeShu
 * @date: 2021-09-17 11:29
 * @Description: API請(qǐng)求返回類
 **/
public class ApiResponse extends HashMap<String, Object> {
    public ApiResponse() {
        put("code", 200);
        put("msg", "success");
    }

    public ApiResponse(String code, String msg) {
        super(2);
        put("code", code);
        put("msg", msg);
    }

    public static ApiResponse ok() {
        ApiResponse r = new ApiResponse();
        r.put("msg", "操作成功");
        return r;
    }

    public static ApiResponse msg(String msg) {
        ApiResponse r = new ApiResponse();
        r.put("msg", msg);
        return r;
    }

    public static ApiResponse ok(Object obj) {
        ApiResponse r = new ApiResponse();
        r.put("code", 200);
        r.put("results", obj);
        return r;
    }

    public static ApiResponse error() {
        return error("500", "未知異常,請(qǐng)聯(lián)系管理員");
    }

    public static ApiResponse error(String msg) {
        return error("500", msg);
    }

    public static ApiResponse error(String code, String msg) {
        ApiResponse r = new ApiResponse();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    @Override
    public ApiResponse put(String key, Object value) {
        super.put(key, value);
        return this;
    }

    /**
     * 分頁
     *
     * @param page
     * @return
     */
    public ApiResponse page(Object page) {
        return put("page", page);
    }


    public ApiResponse result(Object obj) {
        super.put("result", obj);
        return this;
    }
}

2.測試

正常
http://localhost:8080/hello/error1

image.png

空指針異常
http://localhost:8080/hello/error2
image.png

業(yè)務(wù)異常
http://localhost:8080/hello/error3
image.png

3.查看日志

image.png

打開error日志


image.png

? 上一章:SpringBoot —— 多線程定時(shí)任務(wù)的實(shí)現(xiàn)(注解配置刊侯、task:annotation-driven配置)
? 下一章:SpringBoot —— 簡單整合Redis實(shí)例及StringRedisTemplate與RedisTemplate對(duì)比和選擇

創(chuàng)作不易长窄,關(guān)注纲菌、點(diǎn)贊就是對(duì)作者最大的鼓勵(lì)翰舌,歡迎在下方評(píng)論留言
求關(guān)注,定期分享Java知識(shí)椅贱,一起學(xué)習(xí)庇麦,共同成長。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末垮媒,一起剝皮案震驚了整個(gè)濱河市航棱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌它抱,老刑警劉巖朴艰,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沫屡,居然都是意外死亡撮珠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門勺届,熙熙樓的掌柜王于貴愁眉苦臉地迎上來免姿,“玉大人榕酒,你說我怎么就攤上這事∥赏瘢” “怎么了辑舷?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵何缓,是天一觀的道長。 經(jīng)常有香客問我碌廓,道長谷婆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任股淡,我火速辦了婚禮廷区,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘埠帕。我一直安慰自己,他們只是感情好叁巨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布呐籽。 她就那樣靜靜地躺著狡蝶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苏章。 梳的紋絲不亂的頭發(fā)上奏瞬,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音硼端,去河邊找鬼并淋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛显蝌,可吹牛的內(nèi)容都是我干的预伺。 我是一名探鬼主播订咸,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼曼尊,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了脏嚷?” 一聲冷哼從身側(cè)響起骆撇,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎父叙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趾唱,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涌乳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甜癞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夕晓。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悠咱,靈堂內(nèi)的尸體忽然破棺而出蒸辆,到底是詐尸還是另有隱情征炼,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布躬贡,位于F島的核電站谆奥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拂玻。R本人自食惡果不足惜酸些,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纺讲。 院中可真熱鬧擂仍,春花似錦、人聲如沸熬甚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡括。三九已至肃廓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诲泌,已是汗流浹背盲赊。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敷扫,地道東北人哀蘑。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像葵第,于是被迫代替她去往敵國和親绘迁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容