拦截器

概述

1、拦截器需要实现 HandlerInterceptor 接口,或继承 HandlerlnterceptorAdapter 类(已弃用)

2、作用:SpringMVC 中的拦截器用于拦截控制器方法的执行

3、拦截器相当于原生 Servlet 过滤器(Filter)

 

HandlerInterceptor 接口

1、preHandle:控制器方法执行之前,执行 preHandle(),其 boolean 类型的返回值表示是否拦截或放行,返回 true 为放行,即调用控制器方法;返回 false 表示拦截,即不调用控制器方法

2、postHandle:控制器方法执行之后,执行 postHandle()

3、afterComplation:处理完视图和模型数据,渲染视图完毕之后,执行 afterComplation()

public interface HandlerInterceptor {

    /*
        处理程序执行前的拦截点。在HandlerMapping确定了一个合适的处理程序对象之后,但在HandlerAdapter调用处理程序之前被调用。
        DispatcherServlet在一个执行链中处理一个处理程序,该执行链由任意数量的拦截器组成,处理程序本身在最后。通过这个方法,每个拦截器可以决定中止执行链,通常是发送一个HTTP错误或写一个自定义的响应。
        注意:特殊考虑适用于异步请求处理。详情见AsyncHandlerInterceptor。
        默认实现返回true。
        形参:
        request - 当前的HTTP请求
        response - 当前的HTTP响应
        handler - 选择执行的处理程序,用于类型和/或实例评估
        返回值:
        如果执行链应该继续执行下一个拦截器或处理程序本身,则为true。否则,DispatcherServlet会假定这个拦截器已经处理了响应本身。
        抛出:
        异常 - 在出现错误时
    */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

        return true;
    }

    /*
        处理程序成功执行后的拦截点。在HandlerAdapter实际调用处理程序之后,但在DispatcherServlet渲染视图之前被调用。可以通过给定的ModelAndView向视图暴露额外的模型对象。
        DispatcherServlet在一个执行链中处理一个处理程序,该执行链由任意数量的拦截器组成,处理程序本身在最后。通过这个方法,每个拦截器都可以对一个执行进行后处理,以执行链的反顺序来应用。
        注意:对于异步请求的处理要有特殊的考虑。更多细节请见AsyncHandlerInterceptor。
        默认实现是空的。
        形参:
        request - 当前的HTTP请求
        response - 当前的HTTP响应
        handler - 开始异步执行的处理程序(或HandlerMethod),用于类型和/或实例检查
        modelAndView - 处理程序返回的ModelAndView(也可以是空)。
        抛出:
        异常 - 发生错误时
    */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                            @Nullable ModelAndView modelAndView) throws Exception {
    }

    /*
        在请求处理完成后的回调,也就是在渲染视图后。将在处理程序执行的任何结果中被调用,因此可以进行适当的资源清理。
        注意:只有当这个拦截器的preHandle方法成功完成并返回true时,才会被调用!
        与postHandle方法一样,该方法将在链中的每个拦截器上以相反的顺序调用,所以第一个拦截器将是最后被调用的。
        注意:特殊考虑适用于异步请求处理。详情见AsyncHandlerInterceptor。
        默认实现是空的。
        形参:
        request - 当前的HTTP请求
        response - 当前的HTTP响应
        handler - 开始异步执行的处理程序(或处理程序方法),用于类型或实例检查
        ex - 处理程序执行时抛出的任何异常,如果有的话;这不包括通过异常解析器处理的异常。
        抛出:
        异常 - 在出现错误时
    */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 @Nullable Exception ex) throws Exception {
    }

}

 

多个拦截器的执行顺序

1、若每个拦截器的 preHandle() 都返回 true

(1)此时多个拦截器的执行顺序和拦截器在 SpringMVC 的配置文件的配置顺序有关

(2)preHandle() 按照配置的顺序执行,而 postHandle()、afterComplation() 会按照配置的反序执行

2、若某个拦截器的 preHandle() 返回 false

(1)返回 false 的拦截器(包括)之前的拦截器的 preHandle() 都会执行,postHandle() 都不执行

(2)返回 false 的拦截器(不包括)之前的拦截器的 afterComplation() 都会执行

 

步骤

1、编写一个拦截器实现 HandlerInterceptor 接口(示例)

@Component
public class FirstInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor -> preHandle");
        return true;
    }

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

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

2、注解类配置拦截器

(1)实现 WebMvcConfigurer 接口的 addInterceptors 方法,拦截器注册到容器中

(2)addPathPatterns 指定拦截规则,若是拦截所有,静态资源也会被拦截

(3)excludePathPatterns 指定放行请求

(4)示例

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new CustomedInterceptor())
                //所有请求都被拦截,包括静态资源
                .addPathPatterns("/**")
                //放行的请求
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); 
    }
}

 

拦截器原理

1、根据当前请求,获取 HandlerExecutionChain(可以处理请求的 handler 以及 handler 的所有拦截器)

2、先顺序执行所有拦截器的 preHandle 方法

(1)如果当前拦截器 prehandle 返回 true,则执行下一个拦截器的 preHandle

(2)如果当前拦截器 prehandle 返回 false,直接倒序执行所有已经执行了的拦截器的 afterCompletion

3、如果任何一个拦截器返回 false,直接跳出不执行目标方法

4、所有拦截器都返回 true,执行目标方法

5、倒序执行所有拦截器 postHandle 方法

6、以上步骤有任何异常,直接倒序触发 afterCompletion

7、完成页面渲染后,倒序触发 afterCompletion

 

源码

1、DispatcherServlet 类

(1)doDispatch 方法

//mappedHandler,执行量,包含拦截器集合、控制器方法
HandlerExecutionChain mappedHandler = null;
//获取 HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
//顺序执行所有拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    //若applyPreHandle返回true,则继续执行之后方法;若applyPreHandle返回false,即任何一个拦截器返回 false,直接return不执行目标方法
    return;
}
//所有拦截器都返回true,执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//倒序执行所有拦截器 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
//处理派发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
//以上步骤有任何异常,直接倒序触发 afterCompletion
catch (Exception ex) {
    triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
    triggerAfterCompletion(processedRequest, response, mappedHandler,
                           new NestedServletException("Handler processing failed", err));
}

(2)processDispatchResult 方法

//完成页面渲染后,倒序触发afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);

2、HandlerExecutionChain 类

(1)applyPreHandle 方法

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //preHandle()按顺序执行,第一个拦截器为SpringMVC内置拦截器
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        //若当前拦截器prehandle返回true,则执行下一个拦截器的preHandle
        if (!interceptor.preHandle(request, response, this.handler)) {
            //若当前拦截器prehandler返回false,直接倒序执行所有已经执行了的拦截器的afterCompletion
            triggerAfterCompletion(request, response, null);
            //该方法返回false
            return false;
        }
        //若某个拦截器返回false,则interceptorIndex为返回false拦截器的索引-1
        this.interceptorIndex = i;
    }
    //所有拦截器返回true,则该方法返回false
    return true;
}

(2)applyPostHandle 方法

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    //postHandle()反序执行
    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

(3)triggerAfterCompletion 方法

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    //applyPreHandle中的interceptorIndex,决定执行起始的拦截器afterCompletion
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        try {
            interceptor.afterCompletion(request, response, this.handler, ex);
        }
        catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
    }
}
posted @ 2022-06-16 22:41  半条咸鱼  阅读(1344)  评论(0编辑  收藏  举报