一篇文章帶你從源碼分析 SpringBoot 的異常處理

一、起因

我們已經知道如果動態頁面和靜態頁面同時定義了異常處理頁面,例如 classpath:/static/error/404.htmlclasspath:/templates/error/404.html 同時存在時,默認使用動態頁面

即完整的錯誤頁面查找方式應該是這樣:發生了400錯誤--》查找動態 400.html 頁面--》查找靜態 400.html --》查找動態 4xx.html--》查找靜態 5xx.html

至於爲什麼有這樣的機制,本篇文章就從源碼來分析一下。

二、源碼分析

首先按兩下“shift”鍵,查詢ErrorMvcAutoConfiguration類:

從其中挑選關鍵的代碼來說一下

首先是DefaultErrorAttributes,如果我們沒有提供則使用系統自帶的,裏面提供了一些異常數據

@Bean
    @ConditionalOnMissingBean(
        value = {ErrorAttributes.class},
        search = SearchStrategy.CURRENT
    )
    public DefaultErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes();
    }

在這裏插入圖片描述
另一個需要注意的是DefaultErrorViewResolver,從名字就能看出來,這是一個錯誤視圖解析器,同樣自己沒有提供,則使用默認的,這個纔是我們分析的關鍵

		@Bean
        @ConditionalOnBean({DispatcherServlet.class})
        @ConditionalOnMissingBean({ErrorViewResolver.class})
        DefaultErrorViewResolver conventionErrorViewResolver() {
            return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
        }

錯誤頁面的查找方式就是由這個方法控制的,進入DefaultErrorViewResolver方法查看:

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
 status, Map<String, Object> model) {
        ModelAndView modelAndView = 
        this.resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) 
        {
            modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
        }

        return modelAndView;
    }

這裏的HttpStatus status, Map<String, Object> model,status 表示頁面狀態響應碼,model 表示頁面異常數據

ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);

將頁面響應狀態碼和異常數據傳入 resolve 方法中,
在這裏插入圖片描述

String errorViewName = "error/" + viewName;

解釋了爲什麼我們的錯誤頁面一定要放在error目錄下,因爲這裏默認加了error前綴
在這裏插入圖片描述

 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);

查看是否有動態頁面模板,如果有則按動態頁面處理,沒有則按靜態頁面處理
這裏解釋了爲什麼先找動態頁面,再找靜態頁面

好的,接下來繼續進入resolveResource,以此爲例,看看找到靜態頁面和動態頁面之後的下一步處理:
在這裏插入圖片描述

 String[] var3 = this.resourceProperties.getStaticLocations();

查找靜態頁面位置:一篇文章帶你從源碼解析 SpringBoot 中的靜態資源存放位置

這就說明對於錯誤的頁面可以存放的位置有四個,別忘放在每個位置的 error 目錄下即可
在這裏插入圖片描述
這是靜態精確查找,如果沒有找到返回null
在這裏插入圖片描述

if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
        }

返回null ,表示精確查找不到,這裏的SERIES_VIEWS.containsKey(status.series()就是模糊查找,比如400,定位的就是4xx,如果模糊匹配成功,則繼續調用resolve,此時傳輸的頁面異常數據沒有改變,但是響應碼變了,分析如上。
在這裏插入圖片描述

三、自定義異常數據

在這裏插入圖片描述
已經分析了 DefaultErrorAttributes 中定義了一些異常數據,是一些默認設置,我們也可以根據需求自定義異常數據顯示
DefaultErrorAttributes 類本身則是在org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration 異常自動配置類中定義的,如果我們沒有提供一個 ErrorAttributes 的實例的話,那麼 Spring Boot 將自動提供一個ErrorAttributes 的實例,也就是 DefaultErrorAttributes

因此有兩種方式實現 ErrorAttributes :直接實現 ErrorAttributes 接口或者繼承 DefaultErrorAttributes(推薦),因爲 DefaultErrorAttributes 中對異常數據的處理已經完成,可以直接使用。

MyErrorAttributes:

@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        //首先拿到已經處理好的異常
        Map<String, Object> map = super.getErrorAttributes(webRequest, options);
        //然後自定義自己想要的部分異常信息
        map.put("error", "這是自定的的信息");
        return map;
    }
}

4xx.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>4xx</h1>
<table border="1">
    <tr>
        <td>path</td>
        <td th:text="${path}"></td>
    </tr>
    <tr>
        <td>error</td>
        <td th:text="${error}"></td>
    </tr>
    <tr>
        <td>message</td>
        <td th:text="${message}"></td>
    </tr>
    <tr>
        <td>timestamp</td>
        <td th:text="${timestamp}"></td>
    </tr>
    <tr>
        <td>status</td>
        <td th:text="${status}"></td>
    </tr>
</table>
</body>
</html>

在這裏插入圖片描述

四、自定義異常視圖

在這裏插入圖片描述
前面已經進行了分析,當我們沒有自定義錯誤異常視圖時會使用SpringBoot默認的。

所以我們可以自定義類繼承 DefaultErrorViewResolver,然後重寫這個 conventionErrorViewResolver這個方法即可。

@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {

    public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
        super(applicationContext, resourceProperties);
    }
    //重寫異常視圖解析
    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("yolo");
        modelAndView.addAllObjects(model);
        return modelAndView;
    }
}

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章