SpringBoot系列教程web篇之全域性異常處理
當我們的後端應用出現異常時,通常會將異常狀況包裝之後再返回給呼叫方或者前端,在實際的專案中,不可能對每一個地方都做好異常處理,再優雅的程式碼也可能丟擲異常,那麼在 Spring 專案中,可以怎樣優雅的處理這些異常呢?
本文將介紹一種全域性異常處理方式,主要包括以下知識點
- @ControllerAdvice Controller 增強
- @ExceptionHandler 異常捕獲
- @ResponseStatus 返回狀態碼
- NoHandlerFoundException 處理(404 異常捕獲)
右鍵檢視原文: SpringBoot系列教程web篇之全域性異常處理
I. 環境搭建
首先得搭建一個 web 應用才有可能繼續後續的測試,藉助 SpringBoot 搭建一個 web 應用屬於比較簡單的活;
建立一個 maven 專案,pom 檔案如下
<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 依賴搞定之後,寫一個程式入口
/**
* Created by @author yihui in 15:26 19/9/13.
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
複製程式碼
II. 異常處理
1. @ControllerAdvice
我們通常利用@ControllerAdvice
配合註解@ExceptionHandler
來實現全域性異常捕獲處理
-
@ControllerAdvice
為所有的 Controller 織入增強方法 -
@ExceptionHandler
標記在方法上,表示當出現對應的異常丟擲到上層時(即沒有被業務捕獲),這個方法會被觸發
下面我們通過例項進行功能演示
a. 異常捕獲
我們定義兩個異常捕獲的 case,一個是除 0,一個是陣列越界異常
@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,ArrayIndexOutOfBoundsException e) throws IOException {
log.info("array index out error!");
return "aryIndexOutOfBounds: " + getThrowableStackInfo(e);
}
}
複製程式碼
在上面的測試中,我們將異常堆疊返回呼叫方
b. 示例服務
增加幾個測試方法
@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];
}
}
複製程式碼
c. 測試說明
例項測試如下,上面我們宣告捕獲的兩種異常被攔截並輸出對應的堆疊資訊;
但是需要注意
- 404 和未捕獲的 500 異常則顯示的 SpringBoot 預設的錯誤頁面;
- 此外我們捕獲返回的 http 狀態碼是 200
2. @ResponseStatus
上面的 case 中捕獲的異常返回的狀態碼是 200,但是在某些 case 中,可能更希望返回更合適的 http 狀態碼,此時可以使用ResponseStatus
來指定
使用方式比較簡單,加一個註解即可
@ResponseBody
@ExceptionHandler(value = ArithmeticException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleArithmetic(HttpServletRequest request,ArithmeticException e)
throws IOException {
log.info("divide error!");
return "divide 0: " + getThrowableStackInfo(e);
}
複製程式碼
3. 404 處理
通過@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 被我們捕獲並返回堆疊資訊
II. 其他
0. 專案
web 系列博文
- 190930-SpringBoot 系列教程 web 篇之 404、500 異常頁面配置
- 190929-SpringBoot 系列教程 web 篇之重定向
- 190913-SpringBoot 系列教程 web 篇之返回文字、網頁、圖片的操作姿勢
- 190905-SpringBoot 系列教程 web 篇之中文亂碼問題解決
- 190831-SpringBoot 系列教程 web 篇之如何自定義引數解析器
- 190828-SpringBoot 系列教程 web 篇之 Post 請求引數解析姿勢彙總
- 190824-SpringBoot 系列教程 web 篇之 Get 請求引數解析姿勢彙總
- 190822-SpringBoot 系列教程 web 篇之 Beetl 環境搭建
- 190820-SpringBoot 系列教程 web 篇之 Thymeleaf 環境搭建
- 190816-SpringBoot 系列教程 web 篇之 Freemaker 環境搭建
- 190421-SpringBoot 高階篇 WEB 之 websocket 的使用說明
- 190327-Spring-RestTemplate 之 urlencode 引數解析異常全程分析
- 190317-Spring MVC 之基於 java config 無 xml 配置的 web 應用構建
- 190316-Spring MVC 之基於 xml 配置的 web 應用構建
- 190213-SpringBoot 檔案上傳異常之提示 The temporary upload location xxx is not valid
專案原始碼
1. 一灰灰 Blog
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
- 一灰灰 Blog 個人部落格 blog.hhui.top
- 一灰灰 Blog-Spring 專題部落格 spring.hhui.top