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

自定義攔截器:java

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

2、HandlerInterceptor APIspring

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

2.preHandle() mvc

(1)調用時機app

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

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

能夠看到,preHander() 方法的調用是在調用目標方法前調用的。性能

同時能夠看到,經過循環,調用了全部的攔截器,而且若是自定義的攔截器的 preHandler() 返回 false 的狀況下,仍是會去調用攔截器的 afterCompletion() 方法。spa

(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 家族

3、實現自定義的攔截器

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 攔截。

4、攔截器的幾個用處

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>
相關文章
相關標籤/搜索