第一节:SpringMVC 拦截器

一、拦截器

  1、拦截器概述

  SpringMVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器可以实现 HandlerInterceptor 接口,也可以继承 HandlerInterceptAdapter 适配器类
  HandlerInterceptor 中有三个方法需要实现:
    (1)preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回 true;如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。
    (2)postHandle():这个方法在业务处理器完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求 request 进行处理。
    (3)afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
   HandlerInterceptor:
    
  HandlerInterceptorAdapter 类实现了 HandlerInterceptor接口,但这三个方法体都是空,如果需要自定义拦截器,可以继承 HandlerInterceptAdapter 类,根据需要重写方法。
 

  2、拦截器执行

    

  拦截器在 DispatcherServlet 处理完请求后,当由控制器处理请求时才执行拦截器:
        (1)preHandle() 在 处理请求前就被调用了,该方法返回一个 boolean值,如果为 ture,表示对请求放行,可以继续执行;如果为 false,表示对请求拦截,不再执行。
        (2)postHandle() 在处理完请求后,即 获取了ModelAndView ,但是在页面渲染前执行;
        (3)afterCompletion() 在完全处理完请求后调用,相当于在 finally 中执行,用于一些资源清理的操作;

二、自定义拦截器

  1、实现 HandlerInterceptor 或者 HandlerInterceptorAdapter 

public class MyFirstInterceptor implements HandlerInterceptor {

    /**
     * 目标方法运行之前运行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyFirstInterceptor... preHandle...");
        return true;
    }


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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyFirstInterceptor... afterCompletion...");
    }
}

  2、在配置文件中注册配置拦截器

    在 SpringMVC 的配置文件中注册这个拦截器,配置这个拦截器来拦截哪些请求的目标方法

    <!--  配置拦截器  -->
    <mvc:interceptors>
        <!--方式一:默认拦截所有请求-->
        <!--配置某个拦截器,默认是拦截所有请求的;-->
        <bean class="com.njf.interceptor.MyFirstInterceptor"></bean>


        <!-- 方式二:此方式要求拦截器类上必须加注解  @Componentent -->
        <!-- <ref bean="myFirstInterceptor" /> -->

        <!-- 方式三:设置自定义拦截方式 -->
        <!--配置某个拦截器更详情的信息-->
        <mvc:interceptor>
            <!--只来拦截 /test01 请求 -->
            <mvc:mapping path="/test01"/>   <!--设置所有的请求都拦截-->
            <!--<mvc:exclude-mapping path="/"/>  去除指定的请求不拦截-->
            <bean class="com.njf.interceptor.MySecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

三、单个拦截器运行流程

  正常流程:

按配置顺序依次执行preHandle(),再执行目标方法的适配器和处理器;

然后再按一开始倒序来执行postHandle(),再渲染视图页面;

然后再倒序执行afterCompletion()

  

  单个拦截器的正常运行流程

    拦截器的 preHandle ---> 目标方法 ---> 拦截器postHandle ---> 页面 ---> 拦截器的 afterCompletion 
 
  拦截器方法执行顺序
  

  断点调试拦截器执行流程

  

   其他流程

    (1)只要 preHandler 不放行就没有以后的流程;
    (2)只要放行了, 无论目标方法是否正确执行完毕,无论到什么页面,afterCompletion 都会执行;(类似于释放资源)

四、多拦截器运行流程

  配置两个拦截器,看一下是如何运行的:

  拦截器一:

public class MyFirstInterceptor implements HandlerInterceptor {

    /**
     * 目标方法运行之前运行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyFirstInterceptor... preHandle...");
        return true;
    }


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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyFirstInterceptor... afterCompletion...");
    }
}

  拦截器二:

public class MySecondInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MySecondInterceptor... preHandle...");
        return true;
    }

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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MySecondInterceptor... afterCompletion...");
    }
}

  配置拦截器:

    <!--  配置拦截器  -->
    <mvc:interceptors>
        <!--方式一:默认拦截所有请求-->
        <!--配置某个拦截器,默认是拦截所有请求的;-->
        <bean class="com.njf.interceptor.MyFirstInterceptor"></bean>


        <!-- 方式二:此方式要求拦截器类上必须加注解  @Componentent -->
        <!-- <ref bean="myFirstInterceptor" /> -->

        <!-- 方式三:设置自定义拦截方式 -->
        <!--配置某个拦截器更详情的信息-->
        <mvc:interceptor>
            <!--只来拦截 /test01 请求 -->
            <mvc:mapping path="/test01"/>   <!--设置所有的请求都拦截-->
            <!--<mvc:exclude-mapping path="/"/>  去除指定的请求不拦截-->
            <bean class="com.njf.interceptor.MySecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

  

  运行结果

  (1)两个都返回 true

MyFirstInterceptor... preHandle...
MySecondInterceptor... preHandle...
test01被调用了
MySecondInterceptor... postHandle...
MyFirstInterceptor... postHandle...
success.jsp显示了
MySecondInterceptor... afterCompletion...
MyFirstInterceptor... afterCompletion...

  (2)第一个返回 true,第二个返回 false

MyFirstInterceptor... preHandle...
MySecondInterceptor... preHandle...
MyFirstInterceptor... afterCompletion...

  (3)第一个返回false,第二个返回true

MyFirstInterceptor... preHandle...

  (4)两个都返回 false

MyFirstInterceptor... preHandle...

  多个拦截器的流程:与 Filter 流程类似

  (1)拦截器的preHandler:是按照顺序执行;

  (2)拦截器的 postHandler:是按照逆序执行;

  (3)拦截器的 afterCompletion:是按照逆序执行;

  (4)已经放行了的拦截器的 afterCompletion 还是会执行;

  多个拦截器的流程:

  (1)全部返回 true(正常流程)

  

  (2)第一个返回 false(出现拦截情况)

   

   

  多个拦截器执行顺序总结

  1、当有多个拦截器时,且都返回为 true时

preHandle: 按照拦截器数组的正向顺序执行
postHandle:按照拦截器数组的反向顺序执行
afterCompletion:按照拦截器数组的反向顺序执行

  

  2、当多个拦截器的 preHandle 有不同的值时

    (1)第一个返回false,第二个返回 false(全部返回 false时)
        只有第一个 preHandler 会执行
 
    (2)第一个返回 true,第二个返回 false(第一个返回 true,后面都返回 false)
        两个(全部)拦截器的 preHandle 都会执行,但是(全部)postHandle都不会执行,而 afterCompletion 只有第一个(返回false的拦截器之前的所有afterCompletion)会执行
 
    (3)第一个返回 false,第二个返回 true
        只有第一个的 preHandle 会执行

  

五、源码分析

  从源码的执行角度分析流程:

  (1)DispatcherServlet 中 doDispatch 方法:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request. 拿到方法的执行链,包含拦截器,目标方法 HandlerExecutionChain
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request. 拿到方法的适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                //拦截器 preHandler 执行位置,有一个拦截器返回false目标方法以后都不会执行;直接跳到 triggerAfterCompletion
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.  适配器执行目标方法
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(request, mv);
                
                
                //目标方法只要正常,就会走到 postHandle,任何期间有异常就会到 afterCompletion 
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            //页面渲染,如果异常也是直接跳到 triggerAfterCompletion
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //页面异常,也会执行 afterCompletion,afterCompletion 总会执行
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

  (2)HandlerExecutionChain 的 applyPreHandler 方法

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (getInterceptors() != null) {
            for (int i = 0; i < getInterceptors().length; i++) {
                HandlerInterceptor interceptor = getInterceptors()[i];
                
                //preHandle() 返回 true 就放行,返回false就拦截
                if (!interceptor.preHandle(request, response, this.handler)) {
                    //执行完 triggerAfterCompletion()
                    triggerAfterCompletion(request, response, null);
                    //返回一个falsefalse
                    return false;
                }
                //记录一下拦截器的索引
                this.interceptorIndex = i;
            }
        }
        return true;
    }

  (3)HandlerExecutionChain 的 applyPostHandler 方法

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        if (getInterceptors() == null) {
            return;
        }
        //逆向执行每个拦截器的 postHandle
        for (int i = getInterceptors().length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = getInterceptors()[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }

  (4)页面渲染

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

        boolean errorView = false;

        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            //页面渲染
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }

        if (mappedHandler != null) {
            //页面正常执行 afterCompletion
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

  (5)HandlerExecutionChain 的 triggerAfterCompletion 方法

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
            throws Exception {

        if (getInterceptors() == null) {
            return;
        }
        //之前保存的拦截器的索引,有记录最后一个放行拦截器的索引,从他开始把之前所有放行的拦截器的 afterCompletion 都执行
        for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = getInterceptors()[i];
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }

七、拦截器与过滤器

  1、拦截器与过滤器的区别?

    (1)拦截器是 DispatcherServlet 去调用处理器的处理方法时执行;
    (2)过滤器是当客户端向服务器发送请求时进行过滤(DispatcherServlet 处理请求之前)

  2、什么用过滤器(Filter),什么时候用拦截器?

    (1)如果某些功能比较复杂,需要其他组件配合完成,就使用拦截器,可以使用 @AutoWired 注入其他组件;需要依赖于Spring环境;

    (2)Filter 比较适用于功能简单,如设置字符编码等,而且脱离Spring也可以运行,是由Tomcat来创建的;

 
原文地址:https://www.cnblogs.com/niujifei/p/15645053.html