1. 程式人生 > >SpringMVC學習記錄(三)--異常處理

SpringMVC學習記錄(三)--異常處理

學習springMVC從大牛那借鑑了很多經驗,感謝ITEYE的大牛

springMVC提供的異常處理主要有兩種方式,一種是直接實現自己的HandlerExceptionResolver,當然這也包括使用Spring已經為我們提供好的SimpleMappingExceptionResolverDefaultHandlerExceptionResolver,另一種是使用註解的方式實現一個專門用於處理異常的Controller——ExceptionHandler。和之前一樣,前者相當於全域性管理的異常,後者只針對當前controller有效

1.全域性異常

1.建立異常並配置

首先定義一個全域性異常類,可以看出這個類會把異常列印在控制檯,並且放入model中,以便可以在jsp頁面上展示出來

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

public class MyExceptionResolver
implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("==============異常開始============="); ex.printStackTrace(); System.out.println("==============異常結束============="
); //設定返回頁面為error.jsp ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>")); return mv; } }

然後在error.jsp中顯示異常

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
  <head>
  <meta charset="utf-8" />
<title>應用程式異常 (500)</title> 
</head> 
<body> 
  <div> 
    <h1>應用程式異常</h1> 
    <p>抱歉!您訪問的頁面出現異常,請稍後重試或聯絡管理員。</p> 
    <p><a href="#">詳 情</a> 
<a href="javascript:history.back(-1)">返 回</a> 
    </p> 
    <div style="display:none;text-align: left;" id="err">${exception }</div>
  </div>
</body> 
</html>

接著在springMVC.xml中配置這個異常

    <!--配置全域性異常類-->
    <bean id="exceptionResolver" class="main.java.controller.MyExceptionResolver"/>

2.使用上面的異常

在控制器裡面模擬呼叫

    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    public String hello(Model model){
        String str = "asbd123";
        //這裡會報NumberFormatException異常,然後spring會自動捕捉這個異常
        int a = Integer.parseInt(str);

        return "hello";
    }

然後訪問localhost:8888/hello,頁面就會顯示如下
這裡寫圖片描述

同樣的再控制檯會輸出我們的異常資訊
這裡寫圖片描述

到此這就是一個全域性異常.

2.區域性異常處理

區域性異常使用使用@ExceptionHandler進行處理,具體如下

使用@ExceptionHandler進行處理有一個不好的地方是進行異常處理的方法必須與出錯的方法在同一個Controller裡面

   //建立處理異常的類,這個類會處理當前控制器下的Myexception這個異常
    @ExceptionHandler(Myexception.class)
    public String getError(Myexception myexception,Model model){
        System.out.println("==============異常開始=============");
        myexception.printStackTrace();
        System.out.println("==============異常結束=============");
        model.addAttribute("exception",myexception.getMessage());
        return "error";
    }
    //當前這個請求會丟擲異常
    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    public String hello(Model model) throws Myexception {
        if (true){
            throw new Myexception("出錯了");
        }

        return "hello";
    }

對應的JSP檢視中會顯示出相應的錯誤資訊

這裡寫圖片描述

對應的控制檯也會輸出相應的資訊

這裡寫圖片描述

3.異常優先順序

既然在SpringMVC中有兩種處理異常的方式,那麼就存在一個優先順序的問題:
當發生異常的時候,SpringMVC會如下處理:

  1. SpringMVC會先從配置檔案找異常解析器HandlerExceptionResolver
  2. 如果找到了異常異常解析器,那麼接下來就會判斷該異常解析器能否處理當前發生的異常
  3. 如果可以處理的話,那麼就進行處理,然後給前臺返回對應的異常檢視
  4. 如果沒有找到對應的異常解析器或者是找到的異常解析器不能處理當前的異常的時候,就看當前的Controller中有沒有提供對應的異常處理器,如果提供了就由Controller自己進行處理並返回對應的檢視
  5. 如果配置檔案裡面沒有定義對應的異常解析器,而當前Controller中也沒有定義的話,那麼該異常就會被丟擲來。

4.spring提供的異常模板

Spring實現了一個SimpleMappingExceptionResolver,這兩者都是繼承自抽象類AbstractHandlerExceptionResolver,而AbstractHandlerExceptionResolver是實現了HandlerExceptionResolver介面的resolveException方法的,並由此抽取出兩個抽象方法,一個是在進行異常處理之前執行的方法prepareResponse(exception, response),一個是進行異常解析的doResolveException(request, response, handler, exception)方法。
SimpleMappingExceptionResolver,顧名思義就是通過簡單的對映關係來決定由哪個檢視來處理當前的錯誤資訊。SimpleMappingExceptionResolver提供了通過異常型別exceptionMappings來進行異常與檢視之間的對映關係,提供了在發生異常時通過statusCodes來對映異常返回的檢視名稱和對應的HttpServletResponse的返回碼。而且可以通過defaultErrorView和defaultErrorCode來指定預設值,defaultErrorView表示當沒有在exceptionMappings裡面找到對應的異常型別時就返回defaultErrorView定義的檢視,defaultErrorCode表示在發生異常時當沒有在檢視與返回碼的對映關係statusCodes裡面找到對應的對映時預設返回的返回碼。在使用SimpleMappingExceptionResolver時,當發生異常的時候,SimpleMappingExceptionResolver將會把當前的異常物件放到自身屬性exceptionAttribute中,當沒有指定exceptionAttribute時,exceptionAttribute就是用預設值exception。

下面試一個其xml程式碼的簡單配置

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="NumberFormatException">number</prop><!-- 表示當丟擲NumberFormatException的時候就返回名叫number的檢視 -->
                <prop key="NullPointerException">null</prop>
            </props>
        </property>
        <property name="defaultErrorView" value="exception"/><!-- 表示當丟擲異常但沒有在exceptionMappings裡面找到對應的異常時 返回名叫exception的檢視-->
        <property name="statusCodes"><!-- 定義在發生異常時檢視跟返回碼的對應關係 -->
            <props>
                <prop key="number">500</prop><!-- 表示在發生NumberFormatException時返回檢視number,然後這裡定義發生異常時檢視number對應的HttpServletResponse的返回碼是500 -->
                <prop key="null">503</prop>
            </props>
        </property>
        <property name="defaultStatusCode" value="404"/><!-- 表示在發生異常時預設的HttpServletResponse的返回碼是多少,預設是200 -->
    </bean>

下面是引發這些異常的測試程式碼

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/null")
    public void testNullPointerException() {
        Blog blog = null;
        //這裡就會發生空指標異常,然後就會返回定義在SpringMVC配置檔案中的null檢視
        System.out.println(blog.getId());
    }

    @RequestMapping("/number")
    public void testNumberFormatException() {
        //這裡就會發生NumberFormatException,然後就會返回定義在SpringMVC配置檔案中的number檢視
        Integer.parseInt("abc");
    }

    @RequestMapping("/default")
    public void testDefaultException() {
        if (1==1)
            //由於該異常型別在SpringMVC的配置檔案中沒有指定,所以就會返回預設的exception檢視
            throw new RuntimeException("Error!");
    }

}

最後在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中的內建物件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返回的錯誤碼資訊,因為前面已經配置了NumberFormatException的錯誤碼返回值為888,所以這裡應該顯示888 --></span>

  </body>
</html>