無論做什么項目,進(jìn)行異常處理都是非常有必要的,而且你不能把一些只有程序員才能看懂的錯誤代碼拋給用戶去看,所以這時候進(jìn)行統(tǒng)一的異常處理,展現(xiàn)一個比較友好的錯誤頁面就顯得很有必要了。跟其他MVC框架一樣,springMVC也有自己的異常處理機(jī)制。
springMVC提供的異常處理主要有兩種方式,一種是直接實現(xiàn)自己的HandlerExceptionResolver,當(dāng)然這也包括使用Spring已經(jīng)為我們提供好的SimpleMappingExceptionResolver和DefaultHandlerExceptionResolver,另一種是使用注解的方式實現(xiàn)一個專門用于處理異常的Controller——ExceptionHandler。
1、實現(xiàn)自己的HandlerExceptionResolver,HandlerExceptionResolver是一個接口,springMVC本身已經(jīng)對其有了一個自身的實現(xiàn)——DefaultHandlerExceptionResolver,該解析器只是對其中的一些比較典型的異常進(jìn)行了攔截,然后返回對應(yīng)的錯誤碼,當(dāng)然你也可以繼承DefaultHandlerExceptionResolver類,然后重寫其中的一些異常處理方法來實現(xiàn)自己的異常處理。
Java代碼
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class ExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// TODO Auto-generated method stub
return new ModelAndView("exception");
}
}
上述的resolveException的第4個參數(shù)表示對哪種類型的異常進(jìn)行處理。因為Exception類是所有異常類的基類,所以如果想根據(jù)異常類型的不同來進(jìn)行不同的處理的話,可以在resolveException方法里面根據(jù)不同的異常類型進(jìn)行不同的處理,返回不同的異常視圖。如:
Java代碼
public class ExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// TODO Auto-generated method stub
if (ex instanceof NumberFormatException) {
//doSomething...
return new ModelAndView("number");
} else if (ex instanceof NullPointerException) {
//doSomething...
return new ModelAndView("null");
}
return new ModelAndView("exception");
}
}
定義了這樣一個異常處理器之后就要在applicationContext中定義這樣一個bean對象,如:
Xml代碼
<bean id="exceptionResolver" class="com.tiantian.xxx.web.handler.ExceptionHandler"/>
Spring除了實現(xiàn)了一個DefaultHandlerExceptionResolver之外,還實現(xiàn)了一個SimpleMappingExceptionResolver,這兩者都是繼承自抽象類AbstractHandlerExceptionResolver,而AbstractHandlerExceptionResolver是實現(xiàn)了HandlerExceptionResolver接口的resolveException方法的,并由此抽取出兩個抽象方法,一個是在進(jìn)行異常處理之前執(zhí)行的方法prepareResponse(exception, response),一個是進(jìn)行異常解析的doResolveException(request, response, handler, exception)方法。SimpleMappingExceptionResolver,顧名思義就是通過簡單的映射關(guān)系來決定由哪個視圖來處理當(dāng)前的錯誤信息。SimpleMappingExceptionResolver提供了通過異常類型exceptionMappings來進(jìn)行異常與視圖之間的映射關(guān)系,提供了在發(fā)生異常時通過statusCodes來映射異常返回的視圖名稱和對應(yīng)的HttpServletResponse的返回碼。而且可以通過defaultErrorView和defaultErrorCode來指定默認(rèn)值,defaultErrorView表示當(dāng)沒有在exceptionMappings里面找到對應(yīng)的異常類型時就返回defaultErrorView定義的視圖,defaultErrorCode表示在發(fā)生異常時當(dāng)沒有在視圖與返回碼的映射關(guān)系statusCodes里面找到對應(yīng)的映射時默認(rèn)返回的返回碼。在使用SimpleMappingExceptionResolver時,當(dāng)發(fā)生異常的時候,SimpleMappingExceptionResolver將會把當(dāng)前的異常對象放到自身屬性exceptionAttribute中,當(dāng)沒有指定exceptionAttribute時,exceptionAttribute就是用默認(rèn)值exception。
以下是一個簡單的例子:
(1)SpringMVC的servlet配置文件中申明一個SimpleMappingExceptionResolver bean,并通過配置屬性exceptionMappings和defaultExceptionView來指定異常和視圖的對應(yīng)關(guān)系。
Xml代碼
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="NumberFormatException">number</prop><!-- 表示當(dāng)拋出NumberFormatException的時候就返回名叫number的視圖 -->
<prop key="NullPointerException">null</prop>
</props>
</property>
<property name="defaultErrorView" value="exception"/><!-- 表示當(dāng)拋出異常但沒有在exceptionMappings里面找到對應(yīng)的異常時 返回名叫exception的視圖-->
<property name="statusCodes"><!-- 定義在發(fā)生異常時視圖跟返回碼的對應(yīng)關(guān)系 -->
<props>
<prop key="number">500</prop><!-- 表示在發(fā)生NumberFormatException時返回視圖number,然后這里定義發(fā)生異常時視圖number對應(yīng)的HttpServletResponse的返回碼是500 -->
<prop key="null">503</prop>
</props>
</property>
<property name="defaultStatusCode" value="404"/><!-- 表示在發(fā)生異常時默認(rèn)的HttpServletResponse的返回碼是多少,默認(rèn)是200 -->
</bean>
(2)如下訪問:
Java代碼
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/null")
public void testNullPointerException() {
Blog blog = null;
//這里就會發(fā)生空指針異常,然后就會返回定義在SpringMVC配置文件中的null視圖
System.out.println(blog.getId());
}
@RequestMapping("/number")
public void testNumberFormatException() {
//這里就會發(fā)生NumberFormatException,然后就會返回定義在SpringMVC配置文件中的number視圖
Integer.parseInt("abc");
}
@RequestMapping("/default")
public void testDefaultException() {
if (1==1)
//由于該異常類型在SpringMVC的配置文件中沒有指定,所以就會返回默認(rèn)的exception視圖
throw new RuntimeException("Error!");
}
}
(3)Jsp頁面中可以訪問到的異常對象,這里以NumberFormatException的返回視圖number.jsp作為示例:
Jsp代碼
<%@ page language="java" import="java.util.*" pageEncoding="GB18030" isErrorPage="true"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'number.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
NumberFormatException. <br>
<%=exception.getMessage() %><br/>
<%=exception %><br/><span style="color: #3366ff;"><!-- 這是JSP中的內(nèi)置對象exception --></span>
<%=request.getAttribute("ex") %><br><span style="color: #3366ff;"><!-- 這是SpringMVC放在返回的Model中的異常對象 --></span>
<%=request.getAttribute("javax.servlet.error.status_code") %><span style="color: #3366ff;"><!-- HttpServletResponse返回的錯誤碼信息,因為前面已經(jīng)配置了NumberFormatException的錯誤碼返回值為888,所以這里應(yīng)該顯示888 --></span>
</body>
</html>
(4)當(dāng)請求/test/number.do的時候會返回定義好的number視圖,返回結(jié)果如下:
2、使用@ExceptionHandler進(jìn)行處理
使用@ExceptionHandler進(jìn)行處理有一個不好的地方是進(jìn)行異常處理的方法必須與出錯的方法在同一個Controller里面
如:
Java代碼
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tiantian.blog.web.servlet.MyException;
@Controller
public class GlobalController {
/**
* 用于處理異常的
* @return
*/
@ExceptionHandler({MyException.class})
public String exception(MyException e) {
System.out.println(e.getMessage());
e.printStackTrace();
return "exception";
}
@RequestMapping("test")
public void test() {
throw new MyException("出錯了!");
}
}
這里在頁面上訪問test方法的時候就會報錯,而擁有該test方法的Controller又擁有一個處理該異常的方法,這個時候處理異常的方法就會被調(diào)用
優(yōu)先級
既然在SpringMVC中有兩種處理異常的方式,那么就存在一個優(yōu)先級的問題:
當(dāng)發(fā)生異常的時候,SpringMVC會如下處理:
(1)SpringMVC會先從配置文件找異常解析器HandlerExceptionResolver
(2)如果找到了異常異常解析器,那么接下來就會判斷該異常解析器能否處理當(dāng)前發(fā)生的異常
(3)如果可以處理的話,那么就進(jìn)行處理,然后給前臺返回對應(yīng)的異常視圖
(4)如果沒有找到對應(yīng)的異常解析器或者是找到的異常解析器不能處理當(dāng)前的異常的時候,就看當(dāng)前的Controller中有沒有提供對應(yīng)的異常處理器,如果提供了就由Controller自己進(jìn)行處理并返回對應(yīng)的視圖
(5)如果配置文件里面沒有定義對應(yīng)的異常解析器,而當(dāng)前Controller中也沒有定義的話,那么該異常就會被拋出來。