欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot?RESTful?應(yīng)用中的異常處理梳理小結(jié)

 更新時(shí)間:2023年05月17日 10:03:28   作者:永順  
這篇文章主要介紹了SpringBoot?RESTful?應(yīng)用中的異常處理梳理小結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

@ControllerAdvice 和 @ExceptionHandler 的區(qū)別

  • ExceptionHandler, 方法注解, 作用于 Controller 級(jí)別. ExceptionHandler 注解為一個(gè) Controler 定義一個(gè)異常處理器.
  • ControllerAdvice, 類(lèi)注解, 作用于 整個(gè) Spring 工程. ControllerAdvice 注解定義了一個(gè)全局的異常處理器.

需要注意的是, ExceptionHandler 的優(yōu)先級(jí)比 ControllerAdvice 高, 即 Controller 拋出的異常如果既可以讓 ExceptionHandler 標(biāo)注的方法處理, 又可以讓 ControllerAdvice 標(biāo)注的類(lèi)中的方法處理, 則優(yōu)先讓 ExceptionHandler 標(biāo)注的方法處理.

處理 Controller 中的異常

為了方便地展示 Controller 異常處理的方式, 我創(chuàng)建了一個(gè)工程 SpringBootRESTfulErrorHandler, 其源碼可以到我的 Github: github.com/yongshun 中找到.

SpringBootRESTfulErrorHandler 工程的目錄結(jié)構(gòu)如下:

首先我們定義了三個(gè)自定義的異常:

BaseException:

public class BaseException extends Exception {
    public BaseException(String message) {
        super(message);
    }
}

MyException1:

public class MyException1 extends BaseException {
    public MyException1(String message) {
        super(message);
    }
}

MyException2:

public class MyException2 extends BaseException {
    public MyException2(String message) {
        super(message);
    }
}

接著我們?cè)?DemoController 中分別拋出這些異常:

@RestController
public class DemoController {
    private Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler");
    @RequestMapping("/ex1")
    public Object throwBaseException() throws Exception {
        throw new BaseException("This is BaseException.");
    }
    @RequestMapping("/ex2")
    public Object throwMyException1() throws Exception {
        throw new MyException1("This is MyException1.");
    }
    @RequestMapping("/ex3")
    public Object throwMyException2() throws Exception {
        throw new MyException2("This is MyException1.");
    }
    @RequestMapping("/ex4")
    public Object throwIOException() throws Exception {
        throw new IOException("This is IOException.");
    }
    @RequestMapping("/ex5")
    public Object throwNullPointerException() throws Exception {
        throw new NullPointerException("This is NullPointerException.");
    }
    @ExceptionHandler(NullPointerException.class)
    public String controllerExceptionHandler(HttpServletRequest req, Exception e) {
        logger.error("---ControllerException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
        return e.getMessage();
    }
}
  • /ex1: 拋出 BaseException
  • /ex2: 拋出 MyException1
  • /ex3: 拋出 MyException2
  • /ex4: 拋出 IOException
  • /ex5: 拋出 NullPointerException

當(dāng) DemoController 拋出未捕獲的異常時(shí), 我們?cè)?GlobalExceptionHandler 中進(jìn)行捕獲并處理:
GlobalExceptionHandler:

@RestController
@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler");
    @ExceptionHandler(value = BaseException.class)
    @ResponseBody
    public Object baseErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("---BaseException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
        return e.getMessage();
    }
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("---DefaultException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
        return e.getMessage();
    }
}

我們看到, GlobalExceptionHandler 類(lèi)有兩個(gè)注解:

  • RestController, 表明 GlobalExceptionHandler 是一個(gè) RESTful Controller, 即它會(huì)以 RESTful 的形式返回回復(fù).
  • ControllerAdvice, 表示 GlobalExceptionHandler 是一個(gè)全局的異常處理器.

在 GlobalExceptionHandler 中, 我們使用了 ExceptionHandler 注解標(biāo)注了兩個(gè)方法:

  • ExceptionHandler(value = BaseException.class): 表示 baseErrorHandler 處理 BaseException 異常和其子異常.
  • ExceptionHandler(value = Exception.class): 表示 defaultErrorHandler 會(huì)處理 Exception 異常和其所用子異常.

要注意的是, 和 try...catch 語(yǔ)句塊, 異常處理的順序也是從具體到一般, 即如果 baseErrorHandler 可以處理此異常, 則調(diào)用此方法來(lái)處理異常, 反之使用 defaultErrorHandler 來(lái)處理異常.

既然我們已經(jīng)實(shí)現(xiàn)了 Controller 的異常處理, 那么接下來(lái)我們就來(lái)測(cè)試一下吧.
在瀏覽器中分別訪(fǎng)問(wèn)這些鏈接, 結(jié)果如下:
/ex1:

/ex2:

/ex3:

/ex4:

/ex5:

可以看到, /ex1, /ex2, /ex3 拋出的異常都由 GlobalExceptionHandler.baseErrorHandler 處理; /ex4 拋出的 IOException 異常由 GlobalExceptionHandler.defaultErrorHandler 處理. 但是 /ex5 拋出的 NullPointerException 異常為什么不是 defaultErrorHandler 處理, 而是由 controllerExceptionHandler 來(lái)處理呢? 回想到 @ControllerAdvice 和 @ExceptionHandler 的區(qū)別 這以小節(jié)中的內(nèi)容時(shí), 我們就知道原因了: 因?yàn)槲覀冊(cè)?DemoController 中使用 ExceptionHandler 注解定義了一個(gè) Controller 級(jí)的異常處理器, 這個(gè)級(jí)別的異常處理器的優(yōu)先級(jí)比全局的異常處理器優(yōu)先級(jí)高, 因此 Spring 發(fā)現(xiàn) controllerExceptionHandler 可以處理 NullPointerException 異常時(shí), 就調(diào)用這個(gè)方法, 而不會(huì)調(diào)用全局的 defaultErrorHandler 方法了.

處理 404 錯(cuò)誤

Spring MVC

SpringBoot 默認(rèn)提供了一個(gè)全局的 handler 來(lái)處理所有的 HTTP 錯(cuò)誤, 并把它映射為 /error. 當(dāng)發(fā)生一個(gè) HTTP 錯(cuò)誤, 例如 404 錯(cuò)誤時(shí), SpringBoot 內(nèi)部的機(jī)制會(huì)將頁(yè)面重定向到 /error 中.
例如下圖中是一個(gè)默認(rèn)的 SpringBoot 404 異常頁(yè)面.

這個(gè)頁(yè)面實(shí)在是太丑了, 我們能不能自定義一個(gè)異常頁(yè)面呢? 當(dāng)然可以了, 并且 SpringBoot 也給我們提示了: This application has no explicit mapping for /error, so you are seeing this as a fallback.因此我們實(shí)現(xiàn)一個(gè) /error 映射的 Controller 即可.

public class HttpErrorHandler implements ErrorController {
    private final static String ERROR_PATH = "/error";
    /**
     * Supports the HTML Error View
     *
     * @param request
     * @return
     */
    @RequestMapping(value = ERROR_PATH, produces = "text/html")
    public String errorHtml(HttpServletRequest request) {
        return "404";
    }
    /**
     * Supports other formats like JSON, XML
     *
     * @param request
     * @return
     */
    @RequestMapping(value = ERROR_PATH)
    @ResponseBody
    public Object error(HttpServletRequest request) {
        return "404";
    }
    /**
     * Returns the path of the error page.
     *
     * @return the error path
     */
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
}

根據(jù)上面代碼我們看到, 為了實(shí)現(xiàn)自定義的 404 頁(yè)面, 我們實(shí)現(xiàn)了 ErrorController 接口:

public interface ErrorController {
    String getErrorPath();
}

這個(gè)接口只有一個(gè)方法, 當(dāng)出現(xiàn) HTTP 錯(cuò)誤時(shí), SpringBoot 會(huì)將頁(yè)面重定向到 getErrorPath 方法返回的頁(yè)面中. 這樣我們就可以實(shí)現(xiàn)自定義的錯(cuò)誤頁(yè)面了.

RESTful API

提供一個(gè)自定義的 "/error" 頁(yè)面對(duì) Spring MVC 的服務(wù)來(lái)說(shuō)自然是沒(méi)問(wèn)題的, 但是如果我們的服務(wù)是一個(gè) RESTful 服務(wù)的話(huà), 這樣做就不行了.
當(dāng)用戶(hù)調(diào)用了一個(gè)不存在的 RESTful API 時(shí), 我們想記錄下這個(gè)異常訪(fǎng)問(wèn), 并返回一個(gè)代表錯(cuò)誤的 JSON 給客戶(hù)端, 這該怎么實(shí)現(xiàn)呢?
我們很自然地想到, 我們可以使用處理異常的那一套來(lái)處理 404 錯(cuò)誤碼.
那么我們來(lái)試一下這個(gè)想法是否可行吧.

奇怪的是, 當(dāng)我們?cè)跒g覽器中隨意輸入一個(gè)路徑時(shí), 代碼并沒(méi)有執(zhí)行到異常處理邏輯中, 而是返回了一個(gè) HTML 頁(yè)面給我們, 這又是怎么回事呢?
原來(lái) Spring Boot 中, 當(dāng)用戶(hù)訪(fǎng)問(wèn)了一個(gè)不存在的鏈接時(shí), Spring 默認(rèn)會(huì)將頁(yè)面重定向到 **/error** 上, 而不會(huì)拋出異常.既然如此, 那我們就告訴 Spring Boot, 當(dāng)出現(xiàn) 404 錯(cuò)誤時(shí), 拋出一個(gè)異常即可. 在 application.properties 中添加兩個(gè)配置:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

上面的配置中, 第一個(gè) spring.mvc.throw-exception-if-no-handler-found 告訴 SpringBoot 當(dāng)出現(xiàn) 404 錯(cuò)誤時(shí), 直接拋出異常. 第二個(gè) spring.resources.add-mappings 告訴 SpringBoot 不要為我們工程中的資源文件建立映射. 這兩個(gè)配置正是 RESTful 服務(wù)所需要的.
當(dāng)加上這兩個(gè)配置后, 我們?cè)賮?lái)試一下:

可以看到, 現(xiàn)在確實(shí)是在 defaultErrorHandler 中處理了.

以上就是SpringBoot RESTful 應(yīng)用中的異常處理梳理小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot RESTful異常的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Mybatis之RowBounds分頁(yè)原理詳解

    Mybatis之RowBounds分頁(yè)原理詳解

    這篇文章主要介紹了Mybatis之RowBounds分頁(yè)原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)

    Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)

    下面小編就為大家?guī)?lái)一篇Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Java8之lambda最佳實(shí)踐_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java8之lambda最佳實(shí)踐_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    在8 里面Lambda是最火的主題,不僅僅是因?yàn)檎Z(yǔ)法的改變,更重要的是帶來(lái)了函數(shù)式編程的思想,我覺(jué)得優(yōu)秀的程序員,有必要學(xué)習(xí)一下函數(shù)式編程的思想以開(kāi)闊思路
    2017-06-06
  • spring學(xué)習(xí)JdbcTemplate數(shù)據(jù)庫(kù)事務(wù)管理

    spring學(xué)習(xí)JdbcTemplate數(shù)據(jù)庫(kù)事務(wù)管理

    這篇文章主要為大家介紹了spring學(xué)習(xí)JdbcTemplate數(shù)據(jù)庫(kù)事務(wù)管理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Spring Boot學(xué)習(xí)入門(mén)之表單驗(yàn)證

    Spring Boot學(xué)習(xí)入門(mén)之表單驗(yàn)證

    表單驗(yàn)證主要是用來(lái)防范小白搞亂網(wǎng)站和一些低級(jí)的黑客技術(shù)。Spring Boot可以使用注解 @Valid 進(jìn)行表單驗(yàn)證。下面這篇文章主要給大家介紹了關(guān)于Spring Boot學(xué)習(xí)入門(mén)之表單驗(yàn)證的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-09-09
  • Springboot讀取外部配置文件,項(xiàng)目部署時(shí)配置讀取不到問(wèn)題及解決

    Springboot讀取外部配置文件,項(xiàng)目部署時(shí)配置讀取不到問(wèn)題及解決

    這篇文章主要介紹了Springboot讀取外部配置文件,項(xiàng)目部署時(shí)配置讀取不到問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • IntelliJ IDEA中SpringBoot項(xiàng)目通過(guò)devtools實(shí)現(xiàn)熱部署的方法

    IntelliJ IDEA中SpringBoot項(xiàng)目通過(guò)devtools實(shí)現(xiàn)熱部署的方法

    這篇文章主要介紹了IntelliJ IDEA中SpringBoot項(xiàng)目通過(guò)devtools實(shí)現(xiàn)熱部署的方法,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • 淺談Java響應(yīng)式系統(tǒng)

    淺談Java響應(yīng)式系統(tǒng)

    第一次聽(tīng)到reactive這個(gè)詞還是在幾年前,偶然了解到了Rxjava這個(gè)項(xiàng)目,仿佛為我打開(kāi)了一扇新的大門(mén),Rxjava是ReactiveX的java實(shí)現(xiàn),ReactiveX家族除了Rxjava還有RxJS, Rx.NET,RxScala等等。
    2021-06-06
  • Java實(shí)現(xiàn)快速排序算法的完整示例

    Java實(shí)現(xiàn)快速排序算法的完整示例

    這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)快速排序算法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解

    java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解

    下面小編就為大家?guī)?lái)一篇java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解。小編覺(jué)得挺好的,現(xiàn)在分享給大家。給大家一個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-03-03

最新評(píng)論