當咱們的後端應用出現異常時,一般會將異常情況包裝以後再返回給調用方或者前端,在實際的項目中,不可能對每個地方都作好異常處理,再優雅的代碼也可能拋出異常,那麼在 Spring 項目中,能夠怎樣優雅的處理這些異常呢?前端
本文將介紹一種全局異常處理方式,主要包括如下知識點java
右鍵查看原文: SpringBoot系列教程web篇之全局異常處理git
<!-- more -->github
首先得搭建一個 web 應用纔有可能繼續後續的測試,藉助 SpringBoot 搭建一個 web 應用屬於比較簡單的活;web
建立一個 maven 項目,pom 文件以下spring
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7</version> <relativePath/> <!-- lookup parent from update --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
依然是通常的流程,pom 依賴搞定以後,寫一個程序入口json
/** * Created by @author yihui in 15:26 19/9/13. */ @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
咱們一般利用@ControllerAdvice
配合註解@ExceptionHandler
來實現全局異常捕獲處理後端
@ControllerAdvice
爲全部的 Controller 織入加強方法@ExceptionHandler
標記在方法上,表示當出現對應的異常拋出到上層時(即沒有被業務捕獲),這個方法會被觸發下面咱們經過實例進行功能演示數組
咱們定義兩個異常捕獲的 case,一個是除 0,一個是數組越界異常websocket
@Slf4j @ControllerAdvice public class GlobalExceptionHandler { public static String getThrowableStackInfo(Throwable e) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); e.printStackTrace(new java.io.PrintWriter(buf, true)); String msg = buf.toString(); try { buf.close(); } catch (Exception t) { return e.getMessage(); } return msg; } @ResponseBody @ExceptionHandler(value = ArithmeticException.class) public String handleArithmetic(HttpServletRequest request, HttpServletResponse response, ArithmeticException e) throws IOException { log.info("divide error!"); return "divide 0: " + getThrowableStackInfo(e); } @ResponseBody @ExceptionHandler(value = ArrayIndexOutOfBoundsException.class) public String handleArrayIndexOutBounds(HttpServletRequest request, HttpServletResponse response, ArrayIndexOutOfBoundsException e) throws IOException { log.info("array index out error!"); return "aryIndexOutOfBounds: " + getThrowableStackInfo(e); } }
在上面的測試中,咱們將異常堆棧返回調用方
增長几個測試方法
@Controller @RequestMapping(path = "page") public class ErrorPageRest { @ResponseBody @GetMapping(path = "divide") public int divide(int sub) { return 1000 / sub; } private int[] ans = new int[]{1, 2, 3, 4}; @ResponseBody @GetMapping(path = "ary") public int ary(int index) { return ans[index]; } }
實例測試以下,上面咱們聲明捕獲的兩種異常被攔截並輸出對應的堆棧信息;
可是須要注意
上面的 case 中捕獲的異常返回的狀態碼是 200,可是在某些 case 中,可能更但願返回更合適的 http 狀態碼,此時可使用ResponseStatus
來指定
使用方式比較簡單,加一個註解便可
@ResponseBody @ExceptionHandler(value = ArithmeticException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String handleArithmetic(HttpServletRequest request, HttpServletResponse response, ArithmeticException e) throws IOException { log.info("divide error!"); return "divide 0: " + getThrowableStackInfo(e); }
經過@ControllerAdvice
配合@ExceptionHandler
能夠攔截 500 異常,若是我但願 404 異常也能夠攔截,能夠如何處理?
首先修改配置文件application.properties
,將NoHandlerFoundException
拋出來
# 出現錯誤時, 直接拋出異常 spring.mvc.throw-exception-if-no-handler-found=true # 設置靜態資源映射訪問路徑,下面兩個二選一, spring.mvc.static-path-pattern=/statics/** # spring.resources.add-mappings=false
其次是定義異常捕獲
@ResponseBody @ExceptionHandler(value = NoHandlerFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String handleNoHandlerError(NoHandlerFoundException e, HttpServletResponse response) { return "noHandlerFound: " + getThrowableStackInfo(e); }
再次測試以下,404 被咱們捕獲並返回堆棧信息
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛