關於Web應用的全局異常處理,上一篇介紹了ControllerAdvice
結合@ExceptionHandler
的方式來實現web應用的全局異常管理;css
本篇博文則帶來另一種並不常見的使用方式,經過實現自定義的HandlerExceptionResolver
,來處理異常狀態html
上篇博文連接: SpringBoot系列教程web篇之全局異常處理 本篇原文: SpringBoot系列教程web篇之自定義異常處理HandlerExceptionResolverjava
首先得搭建一個web應用纔有可能繼續後續的測試,藉助SpringBoot搭建一個web應用屬於比較簡單的活;git
建立一個maven項目,pom文件以下github
<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>
複製代碼
HandlerExceptionResolver
顧名思義,就是處理異常的類,接口就一個方法,出現異常以後的回調,四個參數中還攜帶了異常堆棧信息web
@Nullable
ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
複製代碼
咱們自定義異常處理類就比較簡單了,實現上面的接口,而後將完整的堆棧返回給調用方spring
public class SelfExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
String msg = GlobalExceptionHandler.getThrowableStackInfo(ex);
try {
response.addHeader("Content-Type", "text/html; charset=UTF-8");
response.getWriter().append("自定義異常處理!!! \n").append(msg).flush();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
// 堆棧信息打印方法以下
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;
}
複製代碼
仔細觀察上面的代碼實現,有下面幾個點須要注意json
response.addHeader("Content-Type", "text/html; charset=UTF-8");
若是沒有這一行,會出現中文亂碼的狀況response.getWriter().append("自定義異常處理!!! \n").append(msg).flush();
; 若是項目中有自定義的錯誤頁面,能夠經過返回ModelAndView
來肯定最終返回的錯誤頁面WebMvcConfigurer
的子類中實現註冊,實例以下@SpringBootApplication
public class Application implements WebMvcConfigurer {
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(0, new SelfExceptionHandler());
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
複製代碼
咱們依然使用上篇博文的用例來測試後端
@Controller
@RequestMapping(path = "page")
public class ErrorPageRest {
@ResponseBody
@GetMapping(path = "divide")
public int divide(int sub) {
return 1000 / sub;
}
}
複製代碼
下面分別是404異常和500異常的實測狀況websocket
500異常會進入咱們的自定義異常處理類, 而404依然走的是默認的錯誤頁面,因此若是咱們須要捕獲404異常,依然須要在配置文件中添加
# 出現錯誤時, 直接拋出異常
spring.mvc.throw-exception-if-no-handler-found=true
# 設置靜態資源映射訪問路徑
spring.mvc.static-path-pattern=/statics/**
# spring.resources.add-mappings=false
複製代碼
爲何404須要額外處理?
下面儘可能以通俗易懂的方式說明下這個問題
@ResponseBody
來代表一個url返回的是json數據(一般狀況下是這樣的,不考慮自定義實現)@Controller
中經過@RequestMapping
定義的REST服務,返回的是靜態資源NoHandlerFoundException
,不拋異常,而是到靜態資源中去找了(靜態資源中也沒有,爲啥不拋NoHandlerFoundException呢?這個異常表示這個url請求沒有對應的處理器,可是咱們這裏呢,給它分配到了靜態資源處理器了ResourceHttpRequestHandler
)針對上面這點,若是有興趣深挖的同窗,這裏給出關鍵代碼位置
// 進入方法: `org.springframework.web.servlet.DispatcherServlet#doDispatch`
// debug 節點
Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 核心邏輯
// org.springframework.web.servlet.DispatcherServlet#getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
複製代碼
本篇博文雖然也介紹了一種新的全局異常處理方式,實現效果和ControllerAdvice
也差很少,可是並不推薦用這種方法, 緣由以下
HandlerExceptionResolver
的方式沒有ControllerAdvice
方式簡介優雅DefaultHandlerExceptionResolver
已經很是強大了,基本上覆蓋了http的各類狀態碼,咱們本身再去定製的必要性不大盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛