1. 程式人生 > >【玩轉SpringBoot】讓錯誤處理重新由web伺服器接管

【玩轉SpringBoot】讓錯誤處理重新由web伺服器接管


其實web伺服器是會處理錯誤的


在web.xml還是隨處可見的年代時(確實有點老黃曆了),下面的這些配置應該都不陌生。

根據錯誤程式碼處理錯誤,如下圖01:


根據異常型別處理錯誤,如下圖02:


不過我們更加熟悉的應該是SpringMVC的統一異常處理。如下圖03:


看到@ControllerAdvice註解和@ExceptionHandler註解都應該很熟悉吧。

處理原理就是在捕獲到業務Controller有異常丟擲時,根據異常型別來這裡找到對應的方法並執行。

可見,整個異常的處理過程都是在SpringMVC內部給搞定了,根本就沒有涉及到web伺服器,如tomcat。

那麼問題來了,明明web伺服器可以處理異常,為啥SpringMVC還要自己處理呢?



是不想呢,還是另有苦衷


讀過本系列上一篇文章的小夥伴應該能猜出來一些。因為SpringMVC開發的工程最終打成war包,然後扔到tomcat下面即可。

而且SpringMVC和tomcat之間是通過Java Web的規範聯絡起來的,它們之間根本沒有辦法自由互動的。

然而tomcat的錯誤處理需要在web.xml裡面配置。嚴格來說,web.xml其實和SpringMVC關係不大。

特別是Spring全面進入Java和註解配置時代以後,web.xml逐漸被弱化,繼而變得可有可無,直到最終完全消失。

所以(我猜測)SpringMVC可能不希望自己的使用者到一個和自己關係不大的web.xml裡面配置一些和業務相關的異常處理對映。

所以只好自己把異常處理消化掉。不勞tomcat大駕。因此才有了SpringMVC統一處理異常。


重新讓web伺服器來處理錯誤


當歷史來到了SpringBoot的時代,SpringBoot翻身做主成了入口,web伺服器竟然成了一個元件。

SpringBoot可以操作web伺服器的API,通過程式設計的方式,對web伺服器進行深度配置。

所以很多事情都變得容易起來,比如錯誤處理。

因為web.xml裡的錯誤處理對映最終是註冊到tomcat裡面了,所以SpringBoot只要操作tomcat的API,使用程式設計的方式也來註冊一些錯誤處理對映不就可以了嘛。

因為使用者直接是和SpringBoot打交道的,所以SpringBoot需要抽象出一套錯誤處理註冊機制,讓使用者來註冊。

這樣SpringBoot拿到使用者註冊的錯誤處理對映資訊後,在生成web伺服器(如tomcat)時,把這些對映資訊新增到web伺服器中即可。


SpringBoot註冊錯誤處理對映的方案


先來看看錯誤處理對映是如何描述的,如下圖04:


三個欄位的含義是:

path是一個路徑,它表示的是錯誤處理的url。

status是一個狀態碼,如404、500等。

exception是一個異常型別,如LoginFailedException。

一共有三種使用方式:

1)status + path,如404 + /404,表示如果遇到了404,就去執行/404這個url。

2)exception + path,如LoginFailedException + /loginfailed,表示如果遇到登陸失敗異常,就去執行後面這個url

3)沒有status和exception,只有path,這相當於萬用字元,匹配所有異常情況。

接下來就該考慮如何註冊了,照例給個介面就行了,如下圖0506:


Spring是以bean打天下的,所以SpringBoot給的方法當然也是和bean相關。

只要向容器中註冊一個該介面型別的bean即可,如下圖07:


這些資訊會被收集到並存好,如下圖08:


然後在建立web伺服器時新增進去就行了,以tomcat為例,如下圖09:


可以看到最終轉換為tomcat的錯誤對映,如下圖10:


這裡的setLocation、setErrorCode、setExceptionType三個setter方法,就對應於web.xml裡的<location>、<error-code>、<exception-type>這三個標籤。

至此,錯誤處理已經被註冊好了。


SpringBoot仍需協助處理錯誤


有一點需要明白,SpringMVC交給tomcat的只是錯誤處理對映的匹配工作,但有些真正的錯誤處理還是要自己做的。

所以整個過程是這樣的,SpringMVC隨意丟擲異常,這個異常會被拋到tomcat裡面,tomcat獲取異常型別並根據註冊的錯誤處理對映關係找到一個url,然後呼叫這個url。

那麼請問這個url指向哪裡呢?很大概率又回到了SpringMVC裡面了,是不是很有意思,哈哈。

tomcat就像一面鏡子,SpringMVC向它發射了一束光線,經過反射後又回來了。只不過發射的是一個異常,回來的是一個url。

這個url對應的是能夠處理這個錯誤的一個Controller的方法。這樣執行這個Controller方法就等於處理了異常。

這個Controller是一個能夠處理錯誤的Controller,所以就叫ErrorController。如下圖11:


其實它主要是一個Marker介面,也就起一個標誌作用。

下面是真正用於處理錯誤的Controller,實現了剛剛的標誌介面,如下圖12:


@RequestMapping方法就是處理錯誤的,既可以返回JSON,也可以返回檢視頁面。

我們可以自己寫一個錯誤處理類,然後繼承這個類,新增自己的錯誤處理方法,最後使用@Controller註解重新註冊即可。

可以把一個異常對映成和它名稱一樣的url路徑,如把Exception對映為/Exception,如下圖13:


這兩個方法,一個返回html頁面,一個返回一個JSON。

備註:異常型別和它對映的url之間的關係,可以按自己的需求去規劃,統一規則即可。

處理錯誤時,自然要獲得錯誤相關資訊才行,這個介面可以滿足,如下圖14:


可以獲得錯誤屬性和丟擲的異常物件。

可以看到介面的兩個方法都有WebRequest這個引數,說明錯誤資訊是從request中獲取出來的。

同時也說明有些錯誤資訊是有人專門放入request中的,是SpringBoot放的,還是web伺服器放的?

其實都有,比如異常物件是SpringBoot放的,響應狀態碼是web伺服器(按Java web規範)放的。

這也說明web伺服器執行錯誤處理的url時用的是轉發(forward)而非重定向(redirect),因為要保留request中的資訊。

還剩最後一個,錯誤檢視解析器,如下圖15:


SpringBoot自己提供了預設的檢視解析器實現,預設去classpath下面的error目錄下尋找.html檢視頁面。

如下圖16:


支援狀態碼到頁面的對映,如404預設對映為/error/404.html,500預設對映為/error/500.html。

如果沒有這麼具體的頁面,還支援系列對映,如404對映為/error/4xx.html,500對映為5xx.html。

當然,預設的一般都無法滿足需求,我們可以繼承這個預設的類,然後重寫檢視解析方法。

最後把這個類註冊為bean即可,這樣SpringBoot就會使用我們的類了。如下圖17:



本文總結,重要


1)使用者使用SpringBoot提供的錯誤處理對映機制註冊狀態碼和異常的對映url資訊。

2)這些對映資訊最終會被註冊進web伺服器中,如tomcat。

3)SpringBoot把異常拋給web伺服器,web伺服器根據異常找到對應的url,並執行它。

4)流程再次回到SpringBoot中,進入錯誤處理Controller的方法中,執行錯誤處理方法。

5)如果需要解析檢視的,使用錯誤檢視解析器進行檢視解析。否則就是直接返回JSON。

其中使用者能參與的就三步,註冊異常對映,擴充套件錯誤處理Controller,擴充套件錯誤檢視解析器。

具體參與方式文章中都有,無非就是實現介面或繼承某個類,然後註冊為bean即可。

 

>>> 玩轉SpringBoot系列文章 <<<

 

【玩轉SpringBoot】配置檔案yml的正確開啟姿勢

【玩轉SpringBoot】用好條件相關注解,開啟自動配置之門

【玩轉SpringBoot】給自動配置來個整體大揭祕

【玩轉SpringBoot】看似複雜的Environment其實很簡單

【玩轉SpringBoot】翻身做主人,一統web伺服器

 

>>> 品Spring系列文章 <<<

 

品Spring:帝國的基石

品Spring:bean定義上梁山

品Spring:實現bean定義時採用的“先進生產力”

品Spring:註解終於“成功上位”

品Spring:能工巧匠們對註解的“加持”

品Spring:SpringBoot和Spring到底有沒有本質的不同?

品Spring:負責bean定義註冊的兩個“排頭兵”

品Spring:SpringBoot輕鬆取勝bean定義註冊的“第一階段”

品Spring:SpringBoot發起bean定義註冊的“二次攻堅戰”

品Spring:註解之王@Configuration和它的一眾“小弟們”

品Spring:bean工廠後處理器的呼叫規則

品Spring:詳細解說bean後處理器

品Spring:對@PostConstruct和@PreDestroy註解的處理方法

品Spring:對@Resource註解的處理方法

品Spring:對@Autowired和@Value註解的處理方法

品Spring:真沒想到,三十步才能完成一個bean例項的建立

品Spring:關於@Scheduled定時任務的思考與探索,結果尷尬了

 

>>> 熱門文章集錦 <<<

相關推薦

SpringBoot錯誤處理重新web伺服器接管

其實web伺服器是會處理錯誤的在web.xml還是隨處可見的年代時(確實有點老黃曆了),下面的這些配置應該都不陌生。根據錯誤程式碼處理錯誤,如下圖01: 根據異常型別處理錯誤,如下圖02: 不過我們更加熟悉的應該是SpringMVC的統一異常處理。如下圖03: 看到@Controlle

SpringBoot翻身做主人,一統web伺服器

寄人籬下的日子一直以來受傳統影響,我們的web工程總是打成war包,然後放入tomcat的webapps目錄下面。如下圖01: 當tomcat啟動時,會去解壓war包,然後執行web工程。這大家都非常熟悉了。用一個抽象的圖形表示,就是這樣子。如下圖02: 在一個大大的tomcat裡面,有一個小

ExcelOracle PLSQL處理生成XLSX檔案

INTRODUCTION介紹    之前發表了一個研究心得(當然是站在別人的肩膀上的),在Oracle中直接用PL/SQL解析並讀取Excel的內容。很多人都感興趣,按照我的寫法也可以成功實現了。不過,有很多朋友提出了另外一個要求:讀取Excel是可以了,那是否可以在Ora

SpringBoot配置檔案yml的正確開啟姿勢

序言在很久以前,Spring的配置檔案是基於XML的。它的名字就是applicationContext.xml,沒錯,就只有這一個xml檔案。它裡面配置了所有的東西。但是資料庫資訊通常會單獨拿出來,放入一個properties檔案,通常叫db.properties。後來覺著一個xml裡的東西實在太多了,就按功

SpringBoot用好條件相關注解,開啟自動配置之門

自動配置隱含兩層含義,要搞清楚上帝讓程式設計師的髮量減少,是為了讓他變得更聰明,如果有一天聰明到了極點,那就是絕頂聰明。據說在大腦高速運轉下,這樣更有利於散熱,不至於核心溫度過高而產生告警。聰明的大腦是用來思考的,現在就來深入思考和分析下自動配置。自動配置包含兩層意思,一個是配置,一個是自動。這不廢話嘛。配置

SpringBoot給自動配置來個整體大揭祕

  上一篇文章中提到的條件註解,只是自動配置整體解決方案中的一個環節而已,可以說是管中窺豹。本文就逐步擦除迷霧,讓整體浮現出來,這樣就會有一個巨集觀的認識。除了寫程式碼之外,還能幹點什麼?提到“配置”這個詞,我們不一定知道它是什麼,但絕對知道它不是什麼,顯然,不是寫程式碼。

SpringBoot看似複雜的Environment其實很簡單

喜歡寫程式碼,討厭配環境我相信這十個字的小標題代表了大多數碼農的心聲。十年前讀大學時,學校開設了C語言還有C++。但是學習這兩種語言,對於新手來說非常沒有成就感。於是我就在校門口買個光碟,裝個VS(宇宙第一IDE),還有離線中文版MSDN(最牛的幫助文件),萬事已俱備。學習C#語法,看類的API,然後從Win

SpringBootSpringBoot應用的啟動過程一覽表

SpringBoot應用的啟動方式很簡單,就一行程式碼,如下圖01: 其實這行程式碼背後主要執行兩個方法,一個是構造方法,一個是run方法。構造方法主要內容就是收集一些資料,和確認一些資訊。如下圖02: 真正的執行要從run方法開始,為此,SpringBoot特意定義了一個監聽器,專門監聽這個

SpringBoot通過事件機制參與SpringBoot應用的啟動過程

生命週期和事件監聽一個應用的啟動過程和關閉過程是歸屬到“生命週期”這個概念的範疇。典型的設計是在啟動和關閉過程中會觸發一系列的“事件”,我們只要監聽這些事件,就能參與到這個過程中來。要想監聽事件,首先得有事件監聽器,就是常說的Listener。下面就是Sprin

SpringBoot非同步任務執行與其執行緒池配置

同步程式碼寫起來簡單,但就是怕遇到耗時操作,會影響效率和吞吐量。此時非同步程式碼才是王者,但涉及多執行緒和執行緒池,以及非同步結果的獲取,寫起來頗為麻煩。不過在遇到SpringBoot非同步任務時,這個問題就不存在了。因為Spring家族是最替使用者考慮的。結果就是,像同步一樣簡單,像非同步一樣強大。眾所熟悉

開源BananaPi R2 —— 第二篇 Openwrt 網口配置分析

sign ati arr asi 1.0 tran spa 們的 errors 上次和大家分享了如何燒錄和安裝Openwrt到BananaPi R2,運行Openwrt的R2目前就具備路由器的功能了,這次我們來看看R2運行Openwrt的性能如何,同時也會講解一些常

開源Linux C 檢測網口熱插拔

int NetDetect(char *net_name, int *statue) { int ret = 0; int skfd = 0; struct ifreq ifr; skfd = socket(AF_INET, SOCK_DGRAM, 0); //建議s

WIN32之旅WINDOWS錯誤處理與參考(四)

    上一篇,我們說到了GetLastError()函式,可是它返回的是一個DWORD(雙字型)的錯誤程式碼,如果我們並不清楚FormatMessage()函式或者就只想快速簡單地得到錯誤

開源制作Docker鏡像

沒有 名稱 登錄 我們 nan utils str oar image 做嵌入式方向經常會遇到的一個問題,就是編譯環境安裝,如果換電腦,再重新安裝環境是一個比較費時的事情,這個時候可以自己制作一個Docker鏡像,然後把編譯環境在Docker鏡像裏面配置好,以後同步環境就非

Ubuntu11. Ubuntu上的瀏覽檔案命令nautilus

瀏覽檔案命令nautilus 先看一下它的幫助命令 [email protected]:~$ nautilus -h 用法: nautilus [選項...] [URI...] Browse the file system with the fil

GridView之簡單實現隱藏列

    做專案中有時候,如果設定了gridview隱藏列,問題就變得很簡單,所以小編總結了兩種實驗過的方法分享給大家。 第一種.     在gridview的RowCreated的方法中設定需要隱藏的

GridView之TemplateField模板

背景介紹:     近期負責開發的子系統中,很大一部分的工作都是和GridView打交道,各種編輯、刪除gridview的操作,所以小編準備系列總結,來進一步學習。     在介紹詳情之前,讓小編帶大家瞭解幾個重要角色。    介紹:在gridview 中單個欄位都會採用

GridView之隱藏域問題

   接著上篇部落格【GridView設定隱藏列方法】來說,這次小編將介紹一種更加方便的方法: 設定隱藏域:    在Gridview加一列使用模板,在模板裡定義隱藏域HiddenField控制元件,

Golangslice切片的操作——切片的追加、刪除、插入等

一、一般操作   1,宣告變數,go自動初始化為nil,長度:0,地址:0,nil func main(){ var ss []string; fmt.Printf("length:%v \taddr:%p \tisnil:%v",len(ss),ss, ss==nil) } -

開源BananaPi R2——移植RPi.GPIO 到 R2

機會 tin 循環輸出 nal 腳本 evel 3.1 我們 api 1. 首先給大家介紹一下什麽是RPi.GPIO. 簡單去講,RPi.GPIO就是一個運行在樹莓派開發板上可以通過Python去控制GPIO的一個中間件。 現在我這邊做了一個基礎功能的移