異常處理-基本介紹
-
基本介紹
1.Spring MVC通過HandlerExceptionResolver處理程序的異常,包括Handler映射、數據綁定以及目標方法執行時發生的異常。
2.主要處理Handler中用@ExceptionHandler注解定義的方法。
3.ExceptionHandlerMethodResolver內部若找不到@ExceptionHandler注解的話,會找@ControllerAdvice類的@ExceptionHandler注解方法,這樣就相當于一個全局異常處理器
異常類型
1.局部異常
在注釋了@Controller的處理器類Handler,內部寫的異常處理,就是局部異常
需要使用注解@在其方法上進行標注
應用實例
處理原因:如果不處理異常,顯示界面會非常的不友好
1.創建com/stein/springMVC/exception/MyExceptionHandler.java
注意:
? ? ? ? 1)Handler要用@Component注入到容器中
? ? ? ? 2)局部異常處理方法,用使用@ExceptionHandler進行注解
? ? ? ? 3)@ExceptionHandler的參數,是填寫異常類型,并且可以是數組的形式
@Controller
public class MyExceptionHandler {//一個正常的方法映射@RequestMapping("/arithmetic")public String exceptionDemo(Integer num){Integer result=100/num;System.out.println("result= "+result);return "success";}@ExceptionHandler({ArithmeticException.class, NullPointerException.class})public String exceptionHandler(Exception exception, HttpServletRequest request){System.out.println("exception= "+exception.getMessage());request.setAttribute("exception", exception.getMessage());return "exception_msg";}}
2.創建操作頁面 web/exception.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>exception</title>
</head>
<body><h1>局部異常處理</h1><a href="<%=request.getContextPath()%>/arithmetic?num=0">點擊顯示算數異常</a>
</body>
</html>
3.創建異常顯示頁面,web/WEB-INF/page/exception_msg.jsp
<body><h1>出錯啦>_<</h1><h2>出錯信息:<%=request.getAttribute("exception")%></h2>
</body>
4.測試
然后顯示出錯信息
Debug代碼
????????異常是通過ExceptionHandlerMethodResolver.java類來處理的,它的異常處理機制比較多。通過Ctrl+N找到
? ? ? ? 通過形參exceptionType拿到異常,然后再拿到處理異常的method,最后通過反射調用。
????????這一步是通過默認的方法查找異常處理方法,沒有找到就返回null
Method method = (Method)this.exceptionLookupCache.get(exceptionType);
? ? ? ? 然后找到了我寫的MyExceptionHandler.exceptionHanler(exception,request)方法
? ? ? ?
????????不出意外的,找到了我寫的異常處理方法
? ? ? ? 最終返回前端頁面進行展示
2.全局異常
應用實例
????????全局異常處理機制:
????????如果在ExceptionHandlerMethodResolver內部找不到@ExceptionHandler注解的話,
????????會去@ControllerAdvice類找@ExceptionHandler注解方法,這樣就相當于一個全局異常處理器
1.創建全局異常類,com/stein/springMVC/exception/GlobalException.java
//加入這個注解后,就表示是一個全局異常
//全局異常就不管是哪個Handler拋出的異常,都可以捕獲
//控制通知
@ControllerAdvice
public class GlobalException {//注解參數用于指定,需要捕獲的異常類型//1. 模擬NumberFormatException//2. 在之前的局部異常中,沒有對NumberFormatException異常進行涵蓋//3. 所以就會來找全局異常處理@ExceptionHandler({NumberFormatException.class, ClassCastException.class})public String global(Exception ex, HttpServletRequest request) {System.out.println("全局異常信息:"+ex.getMessage());request.setAttribute("exception", ex.getMessage());return "exception_msg";}
}
2.增加出現轉換異常的方法,com/stein/springMVC/exception/MyExceptionHandler.java
@RequestMapping("/global")public String globalExceptionDemo(String num){int i = Integer.parseInt(num);return "success";}
3.修改頁面,增加裝換異常訪問請求exception.jsp
<body><h1>異常處理</h1><a href="<%=request.getContextPath()%>/arithmetic?num=0">點擊顯示局部異常</a><br><a href="<%=request.getContextPath()%>/global?num=hello">點擊顯示全局異常</a>
</body>
4.測試
? ? ? ? 選擇全局異常
? ? ? ? 顯示全局異常提示信息
5.postman測試
Debug處理流程
? ? ? ? 依然點擊全局異常
? ? ? ? 斷點不變,可以看到發生了數字格式化異常,這一步method沒有找到處理異常方法,為null
? ? ? ? 看到此時,method找不到在@Controller類中的局部異常處理方法,顯示noMatchingExceptionHandler,沒有匹配的異常處理
? ? ? ? 找到了全局異常處理方法
? ? ? ? ?進入到處理方法
? ? ? ? 最終成功顯示異常信息
注意事項與細節
????????異常處理時:局部異常優先級高于全局異常
? ? ? ? 給局部異常處理添加上NumberFormatException.class后,局部異常優先處理
????????局部異常exception= For input string: "hello"
3.自定義異常(類型)
? ? ? ? 局部異常和全局異常,是通過作用范圍來區分的。
? ? ? ? 自定義異常,說的是自定義異常的類型。比如有ArithmeticException.class, NullPointerException.class,NumberFormatException.class這些異常了,我還需要自定義一個年齡異常AgeException.class
????????通過@ResponseStatus注解,可以自定義該異常
? ? ? ? 關系:那么自定義異常(類型),可以通過默認tomcat調用,或者局部異常、全局異常進行選擇調用。像這樣:
@ExceptionHandler({ArithmeticException.class, NullPointerException.class,NumberFormatException.class,ArithmeticException.class})
應用實例
1.創建自定義異常類型,com/stein/springMVC/exception/AgeException.java
//reason表示顯示的錯誤原因
//value填寫Http錯誤狀態,是枚舉類型的
@ResponseStatus(value = HttpStatus.BAD_REQUEST,reason = "年齡需要在1-120歲之間")
public class AgeException extends RuntimeException {
}
2.添加可能出現自定義異常的方法,MyExceptionHandler.java
@RequestMapping("/age")public String age(int age){if(age>0 && age<120){return "success";}else {throw new AgeException();}}
3.添加訪問鏈接,exception.jsp
<a href="<%=request.getContextPath()%>/age?age=121">點擊顯示自定義異常</a><br><br>
4.測試
? ? ? ? 自定義異常(類型)
????????返回結果
這個tomcat默認頁面太生硬,想用自己頁面顯示
1.把AgeException.class添加到GlobalException.global()的注解中
@ExceptionHandler({NumberFormatException.class, ClassCastException.class,AgeException.class})
2.為了配合exception_msg.jsp的屬性exception.getMessage()返回結果不為null,添加AgeException.java的構造器
@ResponseStatus(value = HttpStatus.BAD_REQUEST,reason = "年齡需要在1-120歲之間")
public class AgeException extends RuntimeException {public AgeException() {}public AgeException(String message) {super(message);}
}
3.業務代碼中,完善message
@RequestMapping("/age")public String age(int age){if(age>0 && age<120){return "success";}else {throw new AgeException("年齡需要在1-120歲之間");}}
4.再次測試
點擊自定義異常后,出現了我們自己寫的異常頁面exception_msg.jsp
同樣,這個自定義異常類型,添加到局部異常也可以生效,這兒就不演示了。
Debug處理流程
? ? ? ? 跟全局異常Debug一樣,參考走一遍即可。
統一處理異常信息
SimpleMappingExceptionResolver
- 基本說明
1.如果希望對所有異常進行統一處理,可以使用SimpleMappingExceptionResolver
2.它將異常類名映射為視圖名,即發生異常時使用對應的視圖報告異常
3.需要在ioc容器中配置
應用實例
1.配置SimpleMappingExceptionResolver
<!--配置一個統一異常處理--><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!--這個屬性用于設置異常映射,即當發生某種異常時,應該跳轉到哪個視圖。--><property name="exceptionMappings"><!--它包含一個`<props>`標簽,這是Spring配置中用于定義`java.util.Properties`類型屬性的方式。每個`<prop>`標簽表示一個鍵值對。--><props><!--鍵(key)是異常類的全限定名--><!--值(value)是`arrEx`,是一個邏輯視圖頁面--><prop key="ArrayIndexOutOfBoundsException">arrEx</prop></props></property></bean>
2.按配置創建異常視圖,web/WEB-INF/page/arrEx.jsp
<h1>朋友,當前頁面異常>_<,如有疑問,可以聯系管理員</h1>
3.增加出現數組越界的映射方法,MyExceptionHandler
@RequestMapping("/array") public String array(){//兩種寫法都可以int[] ints = {1, 3, 6, 12};//int[] ints =new int[]{1,3,6,12};//越界異常System.out.println("ints[5]="+ints[5]);return "success"; }
4.添加訪問頁面的鏈接,exception.jsp
<a href="<%=request.getContextPath()%>/array">點擊測試統一異常處理</a><br><br>
5.測試
對未知異常統一處理
? ? ? ? 異常的情況有很多,當無法對每一種異常都進行單獨配置的時候,需要一種兜底的方案,就是對其他異常的統一處理。
應用實例
1.使用SimpleMappingExceptionResolver進行配置,web/WEB-INF/springMVC-servlet.xml
<!--配置一個統一異常處理--><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!--這個屬性用于設置異常映射,即當發生某種異常時,應該跳轉到哪個視圖。--><property name="exceptionMappings"><!--它包含一個`<props>`標簽,這是Spring配置中用于定義`java.util.Properties`類型屬性的方式。每個`<prop>`標簽表示一個鍵值對。--><props><!--鍵(key)是異常類的全限定名--><!--值(value)是`arrEx`,是一個邏輯視圖頁面--><prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop><!--兩種寫法都可以,但是推薦上面一種,避免重名--><!--<prop key="ArrayIndexOutOfBoundsException">arrEx</prop>--><!--配置其他的未知異常--><!--key配置的是所有異常Exception--><!--響應的頁面名稱是allEx--><prop key="java.lang.Exception">allEx</prop></props></property></bean>
2.創建響應頁面,web/WEB-INF/page/allEx.jsp
<h1>哎呀,發生了未知異常>_<</h1>
3.添加未知異常映射方法,MyExceptionHandler.java
? ? ? ? 這是一個字符串越界異常
@RequestMapping("/unknown")public String unknownException(){String str="hello";System.out.println("str.charAt[5]="+str.charAt(5));return "success";}
4.添加訪問鏈接,exception.jsp
<a href="<%=request.getContextPath()%>/unknown">配置統一的未知異常處理</a><br><br>
5.測試
哎呀,發生了未知異常>_<
異常處理的優先級
???????
?局部異常 > 全局異常 > SimpleMappingExceptionResolver > tomcat默認機制
1.對比測試局部異常、全局異常、SimpleMappingExceptionResolver的優先級,他們3個的范圍都加上ArrayIndexOutOfBoundsException.class,然后進行數組越界異常測試。
? ? ? ? 結果響應的只有局部異常:
局部異常exception= 5
2.接下來取消局部異常的參賽資格,取消其ArrayIndexOutOfBoundsException.class的設置,將剩下2個的進行對比。然后進行數組越界異常測試。
? ? ? ? 此時,響應的是全局異常
全局異常信息:5
3.然后全局異常退出,刪除其越界異常的設置,只保留SimpleMappingExceptionResolver的設置,進行越界測試。
? ? ? ? 結果是按照SimpleMappingExceptionResolver配置的arrEx.jsp進行了響應
朋友,當前頁面異常>_<,如有疑問,可以聯系管理員
4.將SimpleMappingExceptionResolver的設置也取消,包括越界異常和所有異常。進行越界測試
? ? ? ? 結果,Tomcat默認的異常機制開始生效了
? ? ? ? 綜上,確認了他們的優先級排序。