day14-異常處理

来源:https://www.cnblogs.com/liyuelian/archive/2023/02/19/17135771.html
-Advertisement-
Play Games

異常處理 1.基本介紹 SpringMVC 通過 HandlerExceptionResolver 處理程式的異常,包括 Handler映射、數據綁定以及目標方法執行時發生的異常 有兩種方案來進行異常處理: a.在本類編寫處理異常的方法,將拋出的異常視為局部異常處理 b.額外編寫處理異常的類,將拋出 ...


異常處理

1.基本介紹

  1. SpringMVC 通過 HandlerExceptionResolver 處理程式的異常,包括 Handler映射、數據綁定以及目標方法執行時發生的異常

  2. 有兩種方案來進行異常處理:

    a.在本類編寫處理異常的方法,將拋出的異常視為局部異常處理

    b.額外編寫處理異常的類,將拋出的異常視為全局異常處理

  3. 主要處理的是 Handler 中使用了 @ExceptionHandler 註解修飾的方法(局部異常處理)

  4. ExceptionHandlerMethodResolver 內部若找不到上述 @ExceptionHandler 註解修飾的方法,就會去找有 @ControllerAdvice 註解修飾的類中含有 @ExceptionHandler 註解的方法,被 @ControllerAdvice 修飾的類稱為全局處理器(全局異常處理)

  5. 異常處理時,局部異常優先順序高於全局異常

2.局部異常

2.1應用實例

(1)創建 MyExceptionHandler.java,在這個 Handler 中模擬各種出現的異常,併在本類添加處理可能出現的異常的方法(局部異常)

局部異常的處理方法只能處理本類中出現的異常。

package com.li.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

/**
 * @author 李
 * @version 1.0
 */
@Controller
public class MyExceptionHandler {
    /**
     * 1.localException()方法處理局部異常
     * 2.指定處理 算術異常、空指針異常
     * 3. Exception ex 本類生成的異常對象,會傳遞給ex,通過ex可以拿到相關信息,
     *      在這裡可以加入業務邏輯
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String localException(Exception ex, HttpServletRequest request) {
        System.out.println("局部異常信息是=" + ex.getMessage());
        //如何將異常的信息帶到下一個頁面(根據你的業務邏輯)
        request.setAttribute("reason", ex.getMessage());
        return "exception_mes";
    }

    /**
     * 1.編寫方法,模擬異常-算術異常
     * 2.如果我們不做異常處理,是由tomcat預設頁面處理
     * @param num
     * @return
     */
    @RequestMapping(value = "/testException01")
    public String test01(Integer num) {
        int i = 9 / num;
        return "success";
    }
}

(2)exception_mes.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>異常信息</title>
</head>
<body>
<h1>異常信息提示</h1>
<h4>${requestScope.reason}</h4>
</body>
</html>

(3)測試,直接訪問 http://localhost:8080/springmvc/testException01?num=0,顯示結果如下:

image-20230219184501559

捕獲到了異常,SpringMVC 底層反射調用了 localException 方法進行異常處理,跳轉到了 exception_mes.jsp 頁面提示異常信息。

如果我們沒有處理異常,預設是由 tomcat 進行異常處理

2.2執行流程

從發生異常到捕獲異常並調用處理方法,局部異常處理的整個執行流程是怎樣的呢?

第一步:瀏覽器訪問目標方法,如果出現異常,會到 ExceptionHandlerMethodResolver 類中執行 resolveMethodByExceptionType() 方法

第二步:執行resolveMethodByExceptionType() 方法,得到處理異常的方法。

@Nullable
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
    //獲取目標方法出現的異常類型 exceptionType
    //然後通過map去找有沒有一個預設的方法去處理這個異常
   Method method = this.exceptionLookupCache.get(exceptionType);
   if (method == null) {//如果沒有預設方法
       //getMappedMethod 得到該異常映射的處理方法,如例子中的 localException() 方法
      method = getMappedMethod(exceptionType);
       //將找到的這個方法作為 該異常的處理方法,放入到map中
      this.exceptionLookupCache.put(exceptionType, method);
   }
    //返回這個方法
   return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
}

第三步:將得到的異常數據放入到該方法的參數中,底層反射調用處理異常的該方法

第四步:根據處理異常方法,執行自定義的業務邏輯。如例子中跳到某個頁面並提示異常信息。

3.全局異常

3.1應用實例

演示全局異常處理機制。

ExceptionHandlerMethodResolver 內部若找不到 @ExceptionHandler 註解的方法,就會找 @ControllerAdvice 類的 @ExceptionHandler 註解方法,這樣就相當於一個全局處理器,全局處理器可以處理不同的 Handler 出現的異常。

(1)自定義一個全局處理器 MyGlobalException.java

package com.li.web.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;

/**
 * @author 李
 * @version 1.0
 * 如果一個類上標註了 @ControllerAdvice,那麼這個類就是一個全局異常處理器
 */
@ControllerAdvice
public class MyGlobalException {
    /**
     * 1.不管是哪個 Handler拋出的異常,全局異常都可以捕獲
     * 2.@ExceptionHandler({這裡是可以捕獲的異常類型})
     * @param ex 接收拋出的異常對象
     * @return
     */
    @ExceptionHandler({NumberFormatException.class, ClassCastException.class})
    public String globalException(Exception ex, HttpServletRequest request) {
        System.out.println("全局異常處理=" + ex.getMessage());
        //業務處理-將異常信息帶到下一個頁面並顯示
        request.setAttribute("reason", ex.getMessage());
        return "exception_mes";
    }
}

(2)在 Handler 的目標方法中模擬異常

package com.li.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

/**
 * @author 李
 * @version 1.0
 */
@Controller
public class MyExceptionHandler {
    //局部異常處理方法
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String localException(Exception ex, HttpServletRequest request) {
        System.out.println("局部異常信息是=" + ex.getMessage());
        //如何將異常的信息帶到下一個頁面(根據你的業務邏輯)
        request.setAttribute("reason", ex.getMessage());
        return "exception_mes";
    }

    //目標方法
    @RequestMapping(value = "testGlobalException")
    public String global() {
        //模擬一個 NumberFormatException異常,若該異常不能在局部異常方法處理,
        //就會被交到全局異常處理器中處理
        int num = Integer.parseInt("hello");
        return "success";
    }
}

(3)exception_mes.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>異常信息</title>
</head>
<body>
<h1>異常信息提示</h1>
<h4>${requestScope.reason}</h4>
</body>
</html>

(3)在瀏覽器中訪問目標方法 http://localhost:8080/springmvc/testGlobalException,可以看到頁面提示了異常信息,同時後臺輸出為 全局異常處理=For input string: "hello",這說明是全局異常處理器捕獲到的異常,局部異常方法沒有捕獲到。

image-20230219194235199 image-20230219194346365

3.2執行流程

從發生異常到捕獲異常並調用處理方法,全局異常處理的流程如下:

(1)瀏覽器訪問目標方法,若出現異常,會到 ExceptionHandlerExceptionResolver 類的 ServletInvocableHandlerMethod() 方法進行處理:

image-20230219202720075

(2)上述方法首先會到 ExceptionHandlerMethodResolver 類中執行 resolveMethod 方法

(3)resolveMethod 方法又調用本類的 resolveMethodByThrowable 方法

image-20230219203001645

(4)resolveMethodByThrowable 繼續調用本類的 resolveMethodByExceptionType() 方法:

image-20230219203115002
@Nullable
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
    //獲取目標方法的異常類型 exceptionType
    //通過map去找處理該異常的預設的方法
   Method method = this.exceptionLookupCache.get(exceptionType);
   if (method == null) {//若沒有預設方法
       //得到該異常映射的處理方法,這一步如果沒有在Handler類中找到處理方法,
       //如局部異常方法不匹配該異常,就會返回一個 noMatchingExceptionHandler() 的方法
      method = getMappedMethod(exceptionType);
       //將找到的這個方法作為 該異常的處理方法,放入到map中
      this.exceptionLookupCache.put(exceptionType, method);
   }
    //若 method為 noMatchingExceptionHandler(),則返回一個 null
   return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
}

(5)如果 resolveMethodByExceptionType() 方法沒有在本類 Handler 中匹配到可以解決出現的異常的方法,就返回 null

@Nullable
public Method resolveMethodByThrowable(Throwable exception) {
    //如果 mothod 返回 null
   Method method = resolveMethodByExceptionType(exception.getClass());
   if (method == null) {
       //獲取出現異常的原因
      Throwable cause = exception.getCause();
      if (cause != null) {
          //根據異常找匹配的方法
         method = resolveMethodByThrowable(cause);
      }
   }
    //如果返回null
   return method;
}

(6)一路返回到 ExceptionHandlerExceptionResolver 類的 ServletInvocableHandlerMethod() 方法,如果獲取到的 method 仍為 null,就會去遍歷帶有 @ControllerAdvice 註解修飾的類(全局處理器),然後全局處理器中根據 @ExceptionHandler 註解,獲取可以處理異常的方法。

這裡的 entry.getKey() 就是 @ControllerAdvice 註解修飾的類對象。

image-20230219203337711

(7)最後反射調用找到的全局處理器中的方法。

4.自定義異常

可以通過 @ResponseStatus 註解,來自定義異常的說明

4.1應用實例

(1)自定義異常

package com.li.web.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @author 李
 * @version 1.0
 */
@ResponseStatus(reason = "年齡需要在1-120之間",value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException{
}

(2)修改 MyExceptionHandler.java,增加方法進行測試

package com.li.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author 李
 * @version 1.0
 */
@Controller
public class MyExceptionHandler {
    @RequestMapping(value = "/testException02")
    public String test02() {
        throw new AgeException();
    }
}

(3)瀏覽器訪問 http://localhost:8080/springmvc/testException02,測試顯示如下:

訪問目標方法,拋出異常,異常的信息就是你指定的信息,狀態碼也是你指定的數據

image-20230219205233333

4.2拓展

自定義異常可以被局部異常處理和全局異常處理接管,只需要在 @ExceptionHandler 註解中添加自定義異常的.class即可

image-20230219205830287

1.當然,如果想要拿到 ex.getMessage() 信息,需要在自定義的異常類創建帶有 message 參數的構造器

自定義異常的 reason,value 屬性是給 tomcat 的預設頁面顯示的,而構造器的 messgae 屬性是傳入對象的

image-20230219210330744

2.然後在創建異常類對象的時候放入提示信息:

image-20230219210706604

自定義異常本質就是異常,它的處理流程由 它被局部異常還是全局異常接管 而定。如果兩者都沒有接管,就會被 tomcat 來處理,然後 tomcat 顯示一個預設的頁面。

5.SimpleMappingExceptionResolver

5.1基本說明

  1. 如果希望對所有異常進行統一處理,可以使用 SimpleMappingExceptionResolver
  2. 它將異常類名映射為視圖名,即發生異常時使用對應的視圖報告異常
  3. 需要在 spring 的容器文件中配置

5.2應用實例

需求:使用 SimpleMappingExceptionResolver 對數據越界異常進行統一處理

(1)修改 MyExceptionHandler.java,增加方法 test03

//模擬數據下標越界異常
@RequestMapping(value = "/testException03")
public String test03() {
    int[] arr = {3, 8, 18, 20};
    System.out.println(arr[99]);
    return "success";
}

(2)在 spring 容器文件配置

<!--配置統一異常處理的bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <!--key為處理的異常的全路徑,
            arrEx為出現異常後要跳轉的頁面(頁面所在的目錄要和你的視圖解析器的前尾碼匹配)-->
            <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
        </props>
    </property>
</bean>

(3)arrEx.jsp,該頁面顯示異常信息

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>數組下標越界異常</title>
</head>
<body>
異常信息:數組下標越界異常
</body>
</html>

(4)瀏覽器訪問目標方法,返回的頁面顯示:跳轉到了 arrEx.jsp 頁面。

image-20230219213550155

目標方法發生異常--->先去局部異常方法處理--->如果不行,到全局異常處理器處理--->如果不行,到容器文件配置統一異常處理的 bean 處理--->都不行,最後由 tomcat 處理

5.3對未知異常進行統一處理

在實際開發中,異常的種類非常多,我們的異常處理方法可能不能捕獲到所有的異常。

如何處理沒有歸類(未知的)的異常?

仍然可以使用 SimpleMappingExceptionResolver 進行處理。只需要在配置的時候,將捕獲的異常範圍擴大,如Exception。

例子

(1)修改 MyExceptionHandler.java,增加方法 test04

//如果發生了沒有歸類的異常,可以給出統一的提示頁面
@RequestMapping(value = "/testException04")
public String test04() {
    String str = "hello";
    //StringIndexOutOfBoundsException
    char c = str.charAt(10);
    return "success";
}

(2)容器文件配置處理異常的bean時,擴大捕獲範圍

<!--配置統一異常處理的bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
            <!--捕獲未知異常-->
            <prop key="java.lang.Exception">allEx</prop>
        </props>
    </property>
</bean>

(3)allEx.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>異常通知</title>
</head>
<body>
<h1>系統發生了未知異常..</h1>
</body>
</html>

(4)瀏覽器訪問目標方法,訪問結果如下:

image-20230219215416439

5.4異常處理的優先順序

局部異常處理 > 全局異常處理 > SimpleMappingExceptionResolver 處理 > tomcat 預設機制處理


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • require.js是一個js腳本載入器(模塊化載入),它遵循AMD(Asynchronous Module Definition)規範,實現js腳本的非同步載入,不阻塞頁面的渲染和其後的腳本的執行,並提供了在載入完成之後的執行相應回調函數的功能。官網下載地址:https://requirejs.or ...
  • 項目背景 最近在準備開發工作流引擎相關模塊,完成表結構設計後開始著手流程設計器的技術選型,調研了眾多開源項目後決定基於jsplumb.js開源庫進行自研開發,保證定製化的便捷性,相關效果圖及項目地址如下 項目地址:https://gitee.com/code2roc/fast-flow-desgio ...
  • ES6的模塊化設計思想是靜態化,也就是說,在編譯的時候確定模塊的依賴關係,以及輸出輸出入的變數。而CommonJS和AMD模塊都是在運行時確定的。ES6的模塊不是對象,而是通過export顯示指定輸出的代碼,再通過import命令輸入。 // 模塊輸入 import { start,address ...
  • Cesium中的entities可以隨時間變化長度高度,主要依賴於CallbackProperty函數。 因為使用CallbackProperty,cesium中一切可視化的要素都可以與時間聯繫起來。 定義:new Cesium.CallbackProperty (callback, isConst ...
  • 實踐環境 Odoo 14.0-20221212 (Community Edition) 需求描述 如下圖,列表網倉記錄詳情頁面(form視圖),編輯內聯視圖中的貨主記錄,為貨主和倉庫欄位搜索,添加過濾條件,具體如下: 添加、編輯貨主時,下拉列表中只展示選取和當網倉記錄所屬公司關聯的貨主,點擊搜索更多 ...
  • 實踐環境 Odoo 14.0-20221212 (Community Edition) 代碼實現 在js腳本函數中調用模型中自定義方法: this._rpc({ model: 'demo.wizard', // 模型名稱,即模型類定義中 _name 的值 method: 'action_select ...
  • 題目來源 45. 跳躍游戲 II 題目詳情 給定一個長度為 n 的 0 索引整數數組 nums。初始位置為 nums[0]。 每個元素 nums[i] 表示從索引 i 向前跳轉的最大長度。換句話說,如果你在 nums[i] 處,你可以跳轉到任意 nums[i + j] 處: 0 <= j <= nu ...
  • 實踐環境 Odoo 14.0-20221212 (Community Edition) 需求描述 如圖,給表單新增一個類似聊天的視窗,當記錄一些表單活動(本例為自動記錄當前記錄狀態變化) 需求實現 模型定義 odoo14\custom\estate\models\estate_property.py ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...