Spring異常處理——詳解
源碼文件鏈接在最後
背景
一個較爲常見的系統,會涉及控制層,服務(業務)層、緩存層、存儲層以及接口調用等,其中每一個環節都不可避免的會遇到各種不可預知的異常需要處理。如果每個步驟都單獨try…catch會使系統顯的很雜亂,可讀性差,維護成本高;常見的方式就是,實現統一的異常處理,從而將各類異常從各個模塊中解耦出來;
測試案例的製作
前端error.jsp代碼
文件名:/springmvc/src/main/webapp/views/error.jsp
代碼:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div align="center">
<h2>ERROR PAGE</h2>
<div>
errorINof:${exception }
</div>
</div>
</body>
</html>
先來個簡單的數組用來測試今天的異常處理
文件名:/springmvc/src/main/java/init/wuji/springboot/mvc/exception/action/SecondExceptionController.java
代碼
package init.wuji.springboot.mvc.exception.action;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SecondExceptionController {
@RequestMapping("/sectstException")
public String sectstException(int nameIndex) {
String[] userNames = {"張三", "Lisi", "小黑"};
System.out.println("hello Exception! userName:" + userNames[nameIndex]);
return "success";
}
}
那個視圖解析器就不說
別return success看不懂
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
配置在了上下文
也就是我的error文件在views/error.jsp
1、在上下文在配置異常處理(較常見)
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="myException"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
exceptionAttribute是exception用來前端接收的名字,源碼中默認爲exception,如果要修改就定義成別的名字
2、@ControllerAdvice全局作用(較常見)
這裏雖說是ControllerAdvice註解,其實是其與ExceptionHandler的組合使用。在上文中可以看到,單獨使用@ExceptionHandler時,其必須在一個Controller中,然而當其與ControllerAdvice組合使用時就完全沒有了這個限制。換句話說,二者的組合達到的全局的異常捕獲處理。
文件名:/springmvc/src/main/java/init/wuji/springboot/mvc/exception/action/MyExceptionHandler.java
代碼:
package init.wuji.springboot.mvc.exception.action;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class MyExceptionHandler{
@ExceptionHandler(Throwable.class)
public ModelAndView handleException(Throwable e) {
ModelAndView mav = new ModelAndView("error");
System.out.println("=======MyhandleException=========");
mav.addObject("exception", e);
return mav;
}
}
運行結果
一般來說全局註解比較常見使用
3、註解ExceptionHandler(局部)
註解ExceptionHandler作用對象爲方法,最簡單的使用方法就是放在controller文件中,詳細的註解定義不再介紹。如果項目中有多個controller文件,通常可以在baseController中實現ExceptionHandler的異常處理,而各個contoller繼承basecontroller從而達到統一異常處理的目的。因爲比較常見,簡單代碼如下:
文件名:/springmvc/src/main/java/init/wuji/springboot/mvc/exception/action/TestExceptionController.java
代碼:
package init.wuji.springboot.mvc.exception.action;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestExceptionController {
@RequestMapping("/tstException")
public String tstException(int nameIndex) {
String[] userNames = {"張三", "Lisi", "小黑"};
System.out.println("hello Exception! userName:" + userNames[nameIndex]);
return "success";
}
/**
*
* 在使用@ExceptionHandler進行異常處理時,不可以將exception直接設置到方法參數中聲明的modle, 必須返回ModelAndView
*
* @param e
* @param map
* @return
*/
@ExceptionHandler
public ModelAndView handleException(Exception e) {
ModelAndView mav = new ModelAndView("error");
System.out.println("=======handleException=========");
mav.addObject("exception", e);
return mav;
}
}
運行結果
=======handleException=========
17:06:20.458 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Resolved [java.lang.ArrayIndexOutOfBoundsException: 5] to ModelAndView [view="error"; model={exception=java.lang.ArrayIndexOutOfBoundsException: 5}]
17:06:20.458 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.DispatcherServlet - Using resolved error view: ModelAndView [view="error"; model={exception=java.lang.ArrayIndexOutOfBoundsException: 5}]
17:06:20.459 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.view.JstlView - View name 'error', model {exception=java.lang.ArrayIndexOutOfBoundsException: 5}
17:06:20.459 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.view.JstlView - Forwarding to [/views/error.jsp]
17:06:20.461 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
4、接口
實現HandlerExceptionResolver接口
HandlerExceptionResolver本身SpringMVC內部的接口,其內部只有resolveException一個方法,通過實現該接口我們可以達到全局異常處理的目的。
只需要將該Bean加入到Spring容器,可以通過Xml配置,也可以通過註解方式加入容器。
總結(異常處理順序)
三種都講完了,下面說一下,當有多個exception時的運行機制,
通過實踐可知,單獨@ExceptionHandle異常處理排在了首位,@ControllerAdvice排在了第二位,也就是優先選擇離異常近的