SpringMVC——自定義攔截器、異常處理以及父子容器配置


自定義攔截器:

一、若想實現自定義攔截器,需要實現 org.springframework.web.servlet.HandlerInterceptor 接口。

二、HandlerInterceptor API

1. 接口中定義了三個方法

2.preHandle() 

(1)調用時機

在 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法中。

org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle 具體的調用過程

可以看到,preHander() 方法的調用是在調用目標方法前調用的。

同時可以看到,通過循環,調用了所有的攔截器,並且如果自定義的攔截器的 preHandler() 返回 false 的情況下,還是會去調用攔截器的 afterCompletion() 方法。

(2)可以獲取到的資源

 HttpServletRequest、HttpServletResponse、以及目標處理器。

3.postHandler() 

(1)調用的時機

可以看到在調用目標方法后,渲染視圖前調用的 postHandler() 方法。

具體的調用過程:

去調用每一個攔截器的 postHandler() 方法,注意循環的方式,可以看到攔截器棧 preHandler() 和 postHandler() 方法調用形成了一個 U 形。 

(2)可以獲取到的資源

HttpServletRequest、HttpServletResponse、目標hanler 處理器、以及返回的 ModelAndView 對象。

3.afterCompletion()

(1)調用時機

可以看到,org.springframework.web.servlet.DispatcherServlet#doDispatch 執行邏輯的過程,捕獲到異常后,最終都是由攔截器的 afterCompletion() 方法進行的處理。

(2)可以獲取到的資源

HttpServletReques, HttpServletResponse, 目標 handler 處理器, 所有的異常信息

4. HandlerInterceptor 家族

三、實現自定義的攔截器

1.繼承 HandlerInterceptorAdapter ,按照實際需求實現對應的方法就ok。

2.在 SpringMVC Config 文件中配置。

e1:

/**
 * @author solverpeng
 * @create 2016-09-01-15:56
 */
public class FirstInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}
<mvc:interceptors>
    <bean class="com.nucsoft.springmvc.interceptor.FirstInterceptor"/>
</mvc:interceptors>

說明:此種配置可以攔截器所有請求。

e2:

/**
 * @author solverpeng
 * @create 2016-09-01-16:08
 */
public class SecondInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor#preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SecondInterceptor#postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SecondInterceptor#afterCompletion");
    }
}
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/test/**"/>
        <bean class="com.nucsoft.springmvc.interceptor.SecondInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

說明:只會攔截路徑以 test 開頭的請求。如:http://localhost:8080/springmvc/test/testInterceptor02 會被 SecondInterceptor 攔截。

四、攔截器的幾個用處

1.性能監控

/**
 * 計算每次請求耗時
 * @author solverpeng
 * @create 2016-09-01-17:46
 */
public class ConsumeTimeInterceptor extends HandlerInterceptorAdapter{
    // SpringMVC 是單例的,所以對於每一次請求,從 ThreadLocal 中獲取。
    private NamedThreadLocal<Long> consumeTime = new NamedThreadLocal<>("consume_time");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        consumeTime.set(System.currentTimeMillis());
        System.out.println("ConsumeTimeInterceptor#preHandle");
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("ConsumeTimeInterceptor#afterCompletion");
        Long threadEnd = System.currentTimeMillis();
        Long threadStart = consumeTime.get();
        Long consume = threadEnd - threadStart;
        if(consume > 10) {
            System.out.println("add to log: " + consume);
        }
    }
}
<mvc:interceptors>
    <bean class="com.nucsoft.springmvc.interceptor.ConsumeTimeInterceptor"/>
    <bean class="com.nucsoft.springmvc.interceptor.FirstInterceptor"/>
</mvc:interceptors>

控制台輸出:

從中也可以看出:攔截器的順序與 SpringMVC 中配置的順序是一致的。

2.登錄檢測

檢測用戶是否登錄。

 

異常處理

1.局部的

在目標 handler 類中,聲明一個有 @ExceptionHandler 標注的方法,通過 @ExceptionHandler 的 value 的屬性來指定需要處理的異常類型。

可以處理的異常可以是一個類型,也可以是多個類型。如:

@ExceptionHandler(value = {ArithmeticException.class})

@ExceptionHandler(value = {ArithmeticException.class, NullPointerException.class})

 

如:

@Controller
public class SpringTest {
    @ExceptionHandler(value = {ArithmeticException.class})
    public String handleArithmeticException(Exception ex) {
        System.out.println("spring test class");
        ex.printStackTrace();
        return "error";
    }
}

handleArithmeticException() 只能處理 SpringTest 控制器中出現的 ArithmeticException 類型的異常。

2.全局的

(1)基於注解

聲明一個統一的異常處理類:

需要對異常處理類添加 @ControllerAdvice 注解,對異常處理方法的標注還和局部的情況一樣。如:

@ControllerAdvice
public class HandlerException {

    @ExceptionHandler({ArithmeticException.class})
    public String handlerArithmeticException(Exception ex) {
        ex.printStackTrace();
        return "error";
    }

}

handlerArithmeticException() 可以處理所有處理器發生的 ArithmeticException 類型的異常。

(2)基於xml

如:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
    id="simpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="ArithmeticException">error</prop>
        </props>
    </property>
</bean>

通過屬性 exceptionMappings 來指定要處理的異常,以及出現異常返回的頁面。

3.優先級

基於xml>局部>全局

4.如何在異常頁面得到異常信息

request.getAttribute("javax.servlet.error.exception")

5.@ResponseStatus

可以對全局的異常類標注 @ResponseStatus 注解,通過其 value 屬性來指定返回的 Http狀態碼。是 HttpStatus 枚舉類型。

來指定返回對應狀態碼的錯誤頁面。

 

父子容器配置

SpringMVC 層容器可以作為 Spring 容器的子容器:

即 Web 層容器可以引用業務層容器的 Bean,而業務層容器卻訪問不到 WEB 層容器 Bean。

SpringMVC Config:

<context:component-scan base-package="com.nucsoft.springmvc" use-default-filters="false">
    <!-- 只掃描 @Controller 和 @ControllerAdvice 注解-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

Spring Config:

<context:component-scan base-package="com.nucsoft.springmvc">
    <!-- 不掃描 @Controller 和 @ControllerAdvice 注解-->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM