溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Spring?Boot如何統(tǒng)一處理全局異常

發(fā)布時(shí)間:2021-12-14 14:06:24 來源:億速云 閱讀:151 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)Spring Boot如何統(tǒng)一處理全局異常的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

    注解的介紹

    @ControllerAdvice

    @ControllerAdvice注解是Spring3.2中新增的注解,學(xué)名是Controller增強(qiáng)器,作用是給Controller控制器添加統(tǒng)一的操作或處理。

    這里ControllerAdvice也可以這么理解,其抽象級(jí)別應(yīng)該是用于對(duì)Controller進(jìn)行切面環(huán)繞的,而具體的業(yè)務(wù)織入方式則是通過結(jié)合其他的注解來實(shí)現(xiàn)的。@ControllerAdvice是在類上聲明的注解,其用法主要有三點(diǎn):

    1.結(jié)合方法型注解@ExceptionHandler,用于捕獲Controller中拋出的指定類型的異常,從而達(dá)到不同類型的異常區(qū)別處理的目的。

    2.結(jié)合方法型注解@InitBinder,用于request中自定義參數(shù)解析方式進(jìn)行注冊(cè),從而達(dá)到自定義指定格式參數(shù)的目的。

    3.結(jié)合方法型注解@ModelAttribute,表示其注解的方法將會(huì)在目標(biāo)Controller方法執(zhí)行之前執(zhí)行。

    從上面的講解可以看出,@ControllerAdvice的用法基本是將其聲明在某個(gè)bean上,然后在該bean的方法上使用其他的注解來指定不同的織入邏輯。不過這里@ControllerAdvice并不是使用AOP的方式來織入業(yè)務(wù)邏輯的,而是Spring內(nèi)置對(duì)其各個(gè)邏輯的織入方式進(jìn)行了內(nèi)置支持。

    針對(duì)聲明@ExceptionHandler 、 @InitBinder或@ModelAttribute方法的類的@Component @ExceptionHandler , @InitBinder在多個(gè)@Controller類之間共享。

    使用@ControllerAdvice注解的類可以明確聲明為 Spring bean 或通過類路徑掃描自動(dòng)檢測(cè)。 所有此類 bean 都根據(jù)Ordered語義或@Order / @Priority聲明進(jìn)行Ordered , Ordered語義優(yōu)先于@Order / @Priority聲明。 然后在運(yùn)行時(shí)按該順序應(yīng)用@ControllerAdvice bean。 但是請(qǐng)注意,實(shí)現(xiàn)PriorityOrdered @ControllerAdvice bean 的PriorityOrdered不高于實(shí)現(xiàn)Ordered @ControllerAdvice bean。 此外, Ordered不適用于范圍內(nèi)的@ControllerAdvice例如,如果這樣的 bean 已被配置為請(qǐng)求范圍或會(huì)話范圍的 bean。 對(duì)于處理異常, @ExceptionHandler將在第一個(gè)具有匹配異常處理程序方法的通知中被選擇。 對(duì)于模型的屬性和數(shù)據(jù)綁定初始化, @ModelAttribute和@InitBinder方法將遵循@ControllerAdvice秩序。

    注意:對(duì)于@ExceptionHandler方法,在特定建議 bean 的處理程序方法中,根異常匹配將優(yōu)先于僅匹配當(dāng)前異常的原因。 但是,與較低優(yōu)先級(jí)建議 bean 上的任何匹配(無論是根還是原因級(jí)別)相比,更高優(yōu)先級(jí)建議上的原因匹配仍然是首選。 因此,請(qǐng)?jiān)诰哂邢鄳?yīng)順序的優(yōu)先建議 bean 上聲明您的主要根異常映射。

    默認(rèn)情況下, @ControllerAdvice ControllerAdvice 中的方法全局應(yīng)用于所有控制器。 使用諸如annotations 、 basePackageClasses和basePackages (或其別名value )之類的選擇器來定義目標(biāo)控制器的更窄子集。 如果聲明了多個(gè)選擇器,則應(yīng)用布爾OR邏輯,這意味著所選控制器應(yīng)至少匹配一個(gè)選擇器。 請(qǐng)注意,選擇器檢查是在運(yùn)行時(shí)執(zhí)行的,因此添加許多選擇器可能會(huì)對(duì)性能產(chǎn)生負(fù)面影響并增加復(fù)雜性。

    @ExceptionHandler攔截異常并統(tǒng)一處理

    配合 @ExceptionHandler注解結(jié)合使用,當(dāng)異常拋到controller層時(shí),可以對(duì)異常進(jìn)行統(tǒng)一的處理,規(guī)定返回的json格式或者跳轉(zhuǎn)到指定的錯(cuò)誤頁面等.

    @ExceptionHandler的作用主要在于聲明一個(gè)或多個(gè)類型的異常,當(dāng)符合條件的Controller拋出這些異常之后將會(huì)對(duì)這些異常進(jìn)行捕獲,然后按照其標(biāo)注的方法的邏輯進(jìn)行處理,從而改變返回的視圖信息。

    用于處理特定處理程序類和/或處理程序方法中的異常的注解。

    使用此注解注釋的處理程序方法允許具有非常靈活的簽名。 它們可能具有以下類型的參數(shù),按任意順序排列:

    異常參數(shù):聲明為一般異?;蚋唧w的異常。 如果注解本身沒有通過其value()縮小異常類型,這也可用作映射提示

    代碼實(shí)現(xiàn)

    自定義異常

    /**
     * 自定義一個(gè)異常類,用于處理我們發(fā)生的業(yè)務(wù)異常
     *
     * @author Promsing(張有博)
     * @version 1.0.0
     * @since 2021/11/27 - 20:14
     */
    public class BizException extends RuntimeException {
     
        private static final long serialVersionUID = 1L;
     
        /**
         * 錯(cuò)誤碼
         */
        protected String errorCode;
        /**
         * 錯(cuò)誤信息
         */
        protected String errorMsg;
     
        public BizException() {
            super();
        }
     
        public BizException(FrontResult errorInfoInterface) {
            super(errorInfoInterface.getCode());
            this.errorCode = errorInfoInterface.getMessage();
            this.errorMsg = errorInfoInterface.getMessage();
        }
     
        public BizException(FrontResult errorInfoInterface, Throwable cause) {
            super(errorInfoInterface.getCode(), cause);
            this.errorCode = errorInfoInterface.getCode();
            this.errorMsg = errorInfoInterface.getMessage();
        }
     
        public BizException(String errorMsg) {
            super(errorMsg);
            this.errorMsg = errorMsg;
        }
     
        public BizException(String errorCode, String errorMsg) {
            super(errorCode);
            this.errorCode = errorCode;
            this.errorMsg = errorMsg;
        }
     
        public BizException(String errorCode, String errorMsg, Throwable cause) {
            super(errorCode, cause);
            this.errorCode = errorCode;
            this.errorMsg = errorMsg;
        }
     
     
        public String getErrorCode() {
            return errorCode;
        }
     
        public void setErrorCode(String errorCode) {
            this.errorCode = errorCode;
        }
     
        public String getErrorMsg() {
            return errorMsg;
        }
     
        public void setErrorMsg(String errorMsg) {
            this.errorMsg = errorMsg;
        }
     
        public String getMessage() {
            return errorMsg;
        }
     
        @Override
        public Throwable fillInStackTrace() {
            return this;
        }
     
    }

    統(tǒng)一異常處理

     import com.tfjy.arbackend.enumtool.ResultCodeEnum;
    import com.tfjy.arbackend.enumtool.ResutlMsgEnum;
    import com.tfjy.arbackend.util.FrontResult;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
     
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.sql.SQLException;
     
    /**
     * 統(tǒng)一異常處理
     *
     * @author Promsing(張有博)
     * @version 1.0.0
     * @since 2021/11/27 - 20:14
     */
    @ControllerAdvice//使用該注解表示開啟了全局異常的捕獲
    public class GlobalExceptionHandler {
        private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
     
        /**
         * 處理自定義的業(yè)務(wù)異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value = BizException.class)
        @ResponseBody
        public  FrontResult bizExceptionHandler(HttpServletRequest req, BizException e){
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("發(fā)生業(yè)務(wù)異常!原因是:{}",e.getErrorMsg());
            return FrontResult.getExceptionResult(e.getErrorCode(),e.getErrorMsg());
        }
     
        /**
         * 處理空指針的異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value =NullPointerException.class)
        @ResponseBody
        public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e)  {
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("發(fā)生空指針異常!原因是:",e);
            return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
        }
     
        /**
         * 處理索引越界異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value =IndexOutOfBoundsException.class)
        @ResponseBody
        public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e){
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("索引越界異常!原因是:",e);
            return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
        }
     
        /**
         * 處理類未找到異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value =ClassNotFoundException.class)
        @ResponseBody
        public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e)  {
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("發(fā)生類未找到異常!原因是:",e);
            return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
        }
     
     
        /**
         * 處理SQL異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value = SQLException.class)
        @ResponseBody
        public FrontResult exceptionHandler(HttpServletRequest req, SQLException e)  {
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("發(fā)生SQL異常!原因是:",e);
            return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
        }
     
        /**
         * 處理IO異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value = IOException.class)
        @ResponseBody
        public FrontResult exceptionHandler(HttpServletRequest req, IOException e)  {
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("發(fā)生IO異常!原因是:",e);
            return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
        }
     
     
        /**
         * 處理其他異常
         * @param req
         * @param e
         * @return
         */
        @ExceptionHandler(value =Exception.class)
        @ResponseBody
        public FrontResult exceptionHandler(HttpServletRequest req, Exception e){
            logger.error("URL : " + req.getRequestURL().toString());
            logger.error("HTTP_METHOD : " + req.getMethod());
            logger.error("未知異常!原因是:",e);
            return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
        }
    }

    前端返回值類

     
     import com.tfjy.arbackend.enumtool.ResultCodeEnum;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
     
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class FrontResult {
        /**
         * 結(jié)果狀態(tài)碼
         */
        private String code;
        /**
         * 響應(yīng)結(jié)果描述
         */
        private String message;
        /**
         * 返回?cái)?shù)據(jù)
         */
        private Object data;
     
        /**
         * 靜態(tài)方法,返回前端實(shí)體結(jié)果
         *
         * @param code    狀態(tài)碼
         * @param message 消息
         * @param data    數(shù)據(jù)
         * @return 前端實(shí)體結(jié)果
         */
        public static FrontResult build(String code, String message, Object data) {
            return new FrontResult(code, message, data);
        }
     
        /**
         * 返回成功的結(jié)果實(shí)體
         *
         * @param message 消息
         * @param data    數(shù)據(jù)
         * @return 實(shí)體
         */
        public static FrontResult getSuccessResult(String message, Object data) {
            FrontResult result = new FrontResult();
            result.code = ResultCodeEnum.SUCCESS.getCode();
            result.message = message;
            result.data = data;
            return result;
        }
     
        /**
         * 返回?zé)o需data的成功結(jié)果實(shí)體
         *
         * @param message 消息內(nèi)容
         * @return 返回結(jié)果
         */
        public static FrontResult getSuccessResultOnlyMessage(String message) {
            FrontResult result = new FrontResult();
            result.code = ResultCodeEnum.SUCCESS.getCode();
            result.message = message;
            result.data = null;
            return result;
        }
     
        /**
         * 獲取一個(gè)異常結(jié)果
         *
         * @param code 錯(cuò)誤碼
         * @param message 自定義異常信息
         * @return FrontResult
         */
        public static FrontResult getExceptionResult(String code, String message) {
            FrontResult result = new FrontResult();
            result.code = code.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getCode() : code;
            result.message = message.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getMsg() : message;
            return result;
        }
    }
    import lombok.AllArgsConstructor;
     
    @AllArgsConstructor
    public enum ResultCodeEnum {
        // 請(qǐng)求成功
        SUCCESS("0000"),
        // 請(qǐng)求失敗
        FAIL("1111"),
        // EXCEL 導(dǎo)入失敗
        EXCEL_FAIL("1000"),
        // userID 為空
        ID_NULL("1001"),
        // 前端傳的實(shí)體為空
        MODEL_NULL("1002"),
        // 更新失敗
        UPDATE_FAIL("1011"),
        // 參數(shù)為空
        PARAM_ERROR("400"),
        // 代碼內(nèi)部異常
        CODE_EXCEPTION("500", "代碼內(nèi)部異常");
     
        /**
         * 狀態(tài)碼
         */
        private String code;
     
        public String getCode() {
            return code;
        }
     
        ResultCodeEnum(String code) {
            this.code = code;
        }
     
        private String msg;
     
        public String getMsg() {
            return msg;
        }
     
    }
     
     
    public enum ResutlMsgEnum {
     
        //查詢成功
        FIND_SUCCESS("查詢成功!"),
        //查詢失敗
        FIND_FAIL("查詢失?。?quot;),
     
        //更新成功
        UPDATE_SUCCESS("更新成功"),
        //更新失敗
        UPDATE_FAIL("更新成功"),
       
        SEND_SUCCESS("發(fā)送成功"),
     
        SEND_FAIL("發(fā)送失敗");
     
     
        private String msg;
     
        ResutlMsgEnum(String msg) {
            this.msg = msg;
        }
     
        public String getMsg() {
            return msg;
        }
    }

    測(cè)試用例

    /**
     * 測(cè)試用例
     *
     * @author Promsing(張有博)
     * @version 1.0.0
     * @since 2021/11/29 - 9:05
     */
    @Api(tags = {"測(cè)試controller"})
    @RequestMapping(value = "/testController")
    @RestController
    public class TestController {
     
       
        @ApiOperation(value = "測(cè)試null")
        @GetMapping(value = "getNull")
        public FrontResult getNull() {
            int length = 0;
     
                String name=null;
                length = name.length();
     
     
            return FrontResult.build(ResultCodeEnum.SUCCESS.getCode(),
                    ResutlMsgEnum.EXECUTE_SUCCESS.getMsg(), length);
        }
    }

    附:Spring Boot默認(rèn)的異常處理機(jī)制

    默認(rèn)情況下,Spring Boot為兩種情況提供了不同的響應(yīng)方式。

    一種是瀏覽器客戶端請(qǐng)求一個(gè)不存在的頁面或服務(wù)端處理發(fā)生異常時(shí),一般情況下瀏覽器默認(rèn)發(fā)送的請(qǐng)求頭中Accept: text/html,所以Spring Boot默認(rèn)會(huì)響應(yīng)一個(gè)html文檔內(nèi)容,稱作“Whitelabel Error Page”。

    Spring?Boot如何統(tǒng)一處理全局異常

    另一種是使用Postman等調(diào)試工具發(fā)送請(qǐng)求一個(gè)不存在的url或服務(wù)端處理發(fā)生異常時(shí),Spring Boot會(huì)返回類似如下的Json格式字符串信息

    {
        "timestamp": "2018-05-12T06:11:45.209+0000",
        "status": 404,
        "error": "Not Found",
        "message": "No message available",
        "path": "/index.html"
    }

    原理也很簡(jiǎn)單,Spring Boot 默認(rèn)提供了程序出錯(cuò)的結(jié)果映射路徑/error。這個(gè)/error請(qǐng)求會(huì)在BasicErrorController中處理,其內(nèi)部是通過判斷請(qǐng)求頭中的Accept的內(nèi)容是否為text/html來區(qū)分請(qǐng)求是來自客戶端瀏覽器(瀏覽器通常默認(rèn)自動(dòng)發(fā)送請(qǐng)求頭內(nèi)容Accept:text/html)還是客戶端接口的調(diào)用,以此來決定返回頁面視圖還是 JSON 消息內(nèi)容。

    相關(guān)BasicErrorController中代碼如下:

    Spring?Boot如何統(tǒng)一處理全局異常

    感謝各位的閱讀!關(guān)于“Spring Boot如何統(tǒng)一處理全局異?!边@篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI