1. 程式人生 > >Springmv知識六------攔截器&異常處理

Springmv知識六------攔截器&異常處理

攔截器

攔截器概念

攔截器的主要作用就是攔截使用者的請求,在所匹配的目標方法之前進行執行。一般情況下,用作許可權驗證來判斷使用者是否登陸,還有就是商城中不登入不讓購買也可以利用攔截器進行驗證。

我們可以自定義攔截器來實現特定的功能,自定義的攔截器必須實現HandlerInterceptor介面
並且實現以下方法:
preHandle():這個方法在業務處理器處理請求之前被呼叫,在該方法中可以對使用者請求Request進行處理。需要注意的是這個方法的返回值,如果我們在該攔截器對請求進行攔截處理後還要呼叫其他的攔截器,或者是業務處理器去進行處理,則返回true;如果我們不再需要呼叫其他的元件去處理請求,則返回false。


postHandle():*在業務處理器執行完目標方法後,但是是在DispatcherServlet向客戶端返回響應前被呼叫*,在該方法中對使用者請求Request進行處理。
afterCompletion():這個方法在DispatcherServlet完全處理完請求後被呼叫,可以在該方法中進行一些資源清理的操作。

配置自定義的攔截器

二步走:
第一步:編寫自定義攔截器
第二步:註冊自定義攔截器

        <!--  配置自定義攔截器 實現 HandlerInterceptor-->

public class FirstIntercept implements HandlerInterceptor{

    /*  該方法在目標方法之前之前進行攔截呼叫
     * 若該方法返回true,則會繼續呼叫後續的攔截器和目標方法
     * 若方法返回false,則會終止後續攔截器和目標方法的執行。
     * preHandle--》目標方法 --》postHandle--》afterCompletion
     * 
     * 許可權 日誌 事務
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        System.out.println("FirstIntercept  preHandle ");
        //return false;
        return true;
    }

    /*呼叫目標方法之後渲染檢視之前
     * 可以對請求域中的屬性或者檢視做出修改
     * */
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler, ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("FirstIntercept  postHandle ");
    }

    /*渲染檢視之後    
     *  釋放資源        
     * */
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
        System.out.println("FirstIntercept  afterCompletion ");
    }   
}

<!--  註冊自定義攔截器  -->
<mvc:interceptors> <bean class="com.wf.springmvc.crud.intercept.FirstIntercept"></bean> <bean class="com.wf.springmvc.crud.intercept.SecondIntercept"></bean> </mvc:interceptors> <!-- 配置自定義攔截器 方法二--> <mvc:interceptors>
<!-- 配置攔截器(不)作用路徑 是requestmapping路徑 --> <mvc:interceptor> <!-- <mvc:exclude-mapping path="/list"/> --> <mvc:mapping path="/list"/> <bean class="com.wf.springmvc.crud.intercept.SecondIntercept"></bean> </mvc:interceptor> </mvc:interceptors>

注意:
在配置攔截器時,我們可以利用【mvc:interceptor】子節點,進行指定攔截器去作用在那些請求上面,例如上面的配置自定義攔截器 方法二 ,其實這樣可以大大優化我們的效能,因為這樣的話,我們可以指定在某些需要攔截器的目標方法上進行攔截,而對於不需要攔截的目標方法則不進行攔截。

攔截器執行順序

對於 單個攔截器,攔截的順序是按照下列順序進行,但是要注意的是此時的preHandle的返回值是true,如果返回值為false,則執行完HandlerAdapter的目標方法 之後就直接進行渲染檢視,而不再進行其他的處理
FirstIntercept的 preHandle—-》HandlerAdapter的目標方法 —-》FirstIntercept的postHandle—-》DispatcherServlet的渲染檢視 —-》FirstIntercept的afterCompletion

對於多個攔截器,執行順序就像剝洋蔥那樣層層遞進。是根據註冊自定義攔截器的順序進行執行。注意的是,下圖中的停止是直接停止,連目標方法都不會執行。
這裡寫圖片描述

異常處理

Springmvc 通過HandlerExceptionResolver介面 處理程式的異常,包括Handler對映,資料繫結以及目標方法執行時發生的異常。

HandlerExceptionResolver介面的實現類有以下幾種,但是我們常用的是四個:
這裡寫圖片描述

其中在我們配置了【mvc:annotation-driven】時,DispatcherServlet 會給我們裝配以下的三個預設的異常實現類,加斷點,檢視【DispatcherServlet 】的this變數中的【handlerExceptionResolver】
這裡寫圖片描述

ExceptionHandlerExceptionResolver

1、ExceptionHandlerExceptionResolver主要處理Handler中用@ExceptionHandler註解定義的方法。
2、對於@ExceptionHandler註解的方法,如果是發生的NullPointException,但是在我們的宣告異常有RuntimeException和Exception,那麼此時會根據異常的最近繼承關係,找繼承深度最淺的那個,(就近原則) 。
3、ExceptionHandlerMethodResolver內部如果找不到@ExceptionHandler註解的話,就會找@ControllerAdvice中的@ExceptionHandler註解方法。

一般情況下,在我們的執行中出現異常的話,直接會在頁面進行報錯顯示,如果我們利用了我們的異常處理,就可以避免這樣。

<!--前臺連結 -->
<a href="TestExceptionHandlerExceptionResolver?i=2">Test ExceptionHandlerExceptionResolver </a> <br>

<!-- 後臺處理-->
    @RequestMapping("TestExceptionHandlerExceptionResolver")
    public String TestExceptionHandlerExceptionResolver(@RequestParam("i") int i){
        System.out.println("result : "+10/i);
        return "success";
    }

當我們執行上述程式碼時,如果我們沒有進行異常處理的話,跳轉的頁面如下:(我們可以吧傳遞引數設定為0,讓其強制出錯)
這裡寫圖片描述

但是如果我們在這個方法下面新增一個異常處理方法,即被@ExceptionHandler註解修飾的方法。則執行效果如下:
新增的程式碼:

    @ExceptionHandler(value={ArithmeticException.class,IOException.class})
    public ModelAndView TestExceptionHandlerExceptionResolver(Exception ex){
        System.out.println("01----出異常了:"+ex);
        ModelAndView model = new ModelAndView("error");
        model.addObject("exception", ex);
        return model;
    }

    @ExceptionHandler({ClassNotFoundException.class,ClassCastException.class})
    public ModelAndView TestExceptionHandlerExceptionResolver02(Exception ex){
        System.out.println("02----出異常了:"+ex);
        ModelAndView model = new ModelAndView("error");
        model.addObject("exception", ex);
        return model;
    }

注意:
1、上述我們定義了兩個@ExceptionHandler修飾的方法,但是一定要注意兩個方法的異常型別不能相同,這樣做的目的,是為了測試,當我們丟擲的異常不在這預定義的異常之中,會進行就近選擇進行處理。

2、@ExceptionHandler 註解 可以指定異常,可以為多個

3、@ExceptionHandler 修飾的方法,不能使用Map作為與前臺互動的資料儲存, 如果希望將錯誤新城傳遞到前臺,我們需要使用ModelAndView 作為檢視返回值 在model中進行異常的互動與顯示

4、@ExceptionHandler 方法有異常的優先順序,一般都是匹配相似度較高的

5、如果出現異常在本類中找不到@ExceptionHandler修飾的方法進行異常處理,則會進入到 有@ControllerAdvice修飾的類中去查詢@ExceptionHandler修飾的方法進行匹配

6、注意@ExceptionHandler可以匹配多個異常,但是在不同的方法中不能處理相同的異常,因為會不知道該用哪個方法進行處理,回報500異常

執行效果圖如下:
這裡寫圖片描述

或者我們單獨寫出來一個類,用來存放異常處理,如下:


@ControllerAdvice
public class HandlerExceptionController {
    @ExceptionHandler({ArithmeticException.class,IOException.class})
    public ModelAndView TestExceptionHandlerExceptionResolver(Exception ex){
        System.out.println("03----ControllerAdvice出異常了:"+ex);
        ModelAndView model = new ModelAndView("error");
        model.addObject("exception", ex);
        return model;
    }
}

注意使用類註解@ControllerAdvice和異常註解@ExceptionHandler。
這樣的話,當發生異常時,異常在本類中找不到@ExceptionHandler修飾的方法進行異常處理,則會進入到 有@ControllerAdvice修飾的類中去查詢@ExceptionHandler修飾的方法進行匹配。

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver一般用於自己制定特定的響應狀態和錯誤資訊資訊顯示上面。一般我們在向外丟擲異常進行處理時,我們可以丟擲我們自己定義的異常類。
在處理器方法中丟擲了上述異常,若【ExceptionHandlerExceptionResolver】不解析我們的異常類,這是由於觸發的自定義異常帶有【@ResponseStatus註解】,因此,會被ResponseStatusExceptionResolver解析到。最後響應【@ResponseStatus註解】屬性值給客戶端。

<!-- 前臺連結  -->

<a href="TestResponseStatusExceptionResolver?i=10">Test ResponseStatusExceptionResolver </a> <br>


<!-- 後臺處理-->

    // 測試ResponseStatusExceptionResolver  注意自定義異常類NameNOTINPasswordException
    @RequestMapping("TestResponseStatusExceptionResolver")
    public String TestResponseStatusExceptionResolver(@RequestParam("i") int i){
        if(i==13 ){
            throw new NameNOTINPasswordException(); // 更改瀏覽器引數i為13
            //  throw new RuntimeErrorException(null);
        }
        System.out.println("TestResponseStatusExceptionResolver ...");
        return "success";
    }

            <!-- 自定義異常類 -->
//    reason指定顯示資訊   value http的狀狀態碼  遮蔽此註解對比  拿到外部瀏覽器進行顯示
//  不要標在方法上面,儘管可以,會造成應該正常的顯示頁面也產生錯誤頁面上

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="使用者名稱與密碼不匹配")
public class NameNOTINPasswordException extends RuntimeException {
    private static final long serialVersionUID = 1L;
}

注意:ResponseStatusExceptionResolver只處理帶有@ResponseStatus註解的異常並將其對映為狀態碼。
第一次我們丟擲RuntimeErrorException,由於沒有註解@ResponseStatus,所以返回的為500
第二次我們修改我們丟擲的異常為自定義的異常,並且利用註解@ResponseStatus,所以返回我們特定的狀態碼和資訊。
這裡寫圖片描述

DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolver這個主要是處理Spring一些特定的異常並且把他們轉化為狀態碼。在原始碼中我們可以看到。

這裡寫圖片描述

測試上述HttpRequestMethodNotSupportedException。他的意思是不支援我們提交的方法方式。
我們的超連結是GET方式,我們在後臺處理時,將方法指定為處理POST請求,這樣的話,就會交給我們的這個DefaultHandlerExceptionResolver進行處理。

<!-- 前臺連結  get 方式 -->
<a href="TestHttpRequestMethodNotSupportedException">Test HttpRequestMethodNotSupportedException </a> <br>

<!--後臺處理  post方式 -->
    @RequestMapping(value="/TestHttpRequestMethodNotSupportedException",method=RequestMethod.POST)
    public String TestHttpRequestMethodNotSupportedException(){
        System.out.println("TestHttpRequestMethodNotSupportedException...");
        return "success";

    } 

這裡寫圖片描述

注意: 顯然,從這裡我們可以看出以前再出現錯誤頁面時,都是經過框架給我們處理過的。

SimpleMappingExceptionResolver

這個異常處理,主要是解決當發生我們所指定的異常情況時,跳轉到我們所指定的頁面。這個異常我們在Springmvc.xml檔案中進行註冊。

<!-- 前臺連結 -->
<a href="TestSimpleMappingExceptionResolver?i=10">Test SimpleMappingExceptionResolver </a> <br>

<!--後臺處理 -->
@RequestMapping("/TestSimpleMappingExceptionResolver")
    public String TestSimpleMappingExceptionResolver(@RequestParam("i") Integer i){
        int [] vals = new int[20];
        System.out.println(vals[i]);  // 傳遞引數 21 讓其發生 java.lang.ArrayIndexOutOfBoundsException
        return"success";
    }


<!--檔案配置 -->

<!-- 配置異常  SimpleMappingExceptionResolver
            在這個頁面中,會將異常資訊儲存到RequestScope域中的exception屬性中,我們可以直接在頁面獲取
            或者我們也可以配置這個exceptionAttribute屬性,將指定 RequestScope域中的特定屬性中
    -->
    <bean class=" org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">  <!--name固定  -->
            <props>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>  
                <!-- 配置指定的異常到指定的位置 -->
            </props>
        </property>

        <property name="exceptionAttribute" value="ex"></property>
            <!-- 配置 特定的域 ,在頁面應該是${ex}-->
    </bean>

<!-- error 頁面 -->

<body>
Error Page <br>
預設儲存域: ${exception }  <br>
haha   <br>
配置修改儲存域:${ex}<br>
</body>

注意:配置指定的異常到指定的位置,依然經過我們的檢視解析器進行配置,所以要在指定位置編寫錯誤頁面。