跳到内容
argsno
返回

Filter(过滤器)和Interceptor(拦截器)

引言

过滤器(Filter)和拦截器(Interceptor)由于功能差不多,都可以用来对请求进行预处理和对响应结果进行处理,可能比较容易搞混,这里从具体的功能和实现原理进行区别一下,做个笔记。

Filter

Filter是Servlet标准的一部分,依赖于servlet容器,主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理调用链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行预处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter有如下几个用处:

Filter有如下几个种类:

创建一个Filter只需两个步骤:

创建Filter必须实现javax.servlet.Filter接口,在该接口中定义了如下三个方法:

实现原理

public interface Filter {
    // 容器创建的时候调用, 即启动tomcat的时候调用
    public void init(FilterConfig filterConfig) throws ServletException;

    // 由FilterChain调用, 并且传入Filter Chain本身
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    // 容器销毁的时候调用, 即关闭tomcat的时候调用
    public void destroy();
}

// FilterChain
public interface FilterChain {
    // 由Filter.doFilter()中的chain.doFilter调用
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException;
}

在tomcat中Filter Chain的默认实现是ApplicationFilterChain, 在ApplicationFilterChain中最关键的方法就是internalDoFilter, 整个Filter流程的实现就是由这个方法完成.

// internalDoFilter(只保留关键代码)
private void internalDoFilter(ServletRequest request, ServletResponse response)
    throws IOException, ServletException {

    // Call the next filter if there is one
    // pos: 当前的filter的索引, n: 调用链中所有的Filter的数量
    // 如果调用链中还有没有调用的Filter就继续调用, 否则跳过if语句
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            // 获取Filter
            Filter filter = filterConfig.getFilter();
            if( Globals.IS_SECURITY_ENABLED ) {
                ...
                其他代码
                ...
            } else {
                // 这句话是重点, 调用Filter的doFilter方法并把Filter Chain本身传进去(this)
                filter.doFilter(request, response, this);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            ...
            异常处理代码
            ...
        }
        return;
    }

    // We fell off the end of the chain -- call the servlet instance
    try {
        ...
        其他代码
        ...
        // Use potentially wrapped request from this point
        if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse) &&
                Globals.IS_SECURITY_ENABLED ) {
            ...
            其他代码
            ...
        } else {
                // 调用真正的Filter
            servlet.service(request, response);
        }
    } catch (IOException | ServletException | RuntimeException e) {
        ...
        异常处理代码
        ...
    } finally {
        ...
        始终要执行的代码
        ...
    }
}

Interceptor

Interceptor 是Spring MVC实现的一部分,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。Spring MVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。

在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。

实现HandlerInterceptor接口

HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。

实现

Interceptor功能的实现主要是在Spring Mvc的DispatcherServlet.doDispatch方法中, 让我们来看看源码

// Interceptor的源码
public interface HandlerInterceptor {

    // 在调用真正的处理请求类之前调用
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

    // 在调用真正的处理请求类之后调用
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

 // 在完成渲染或者出错之后调用
    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

}
    // doDispatch源码(只保留关键代码)
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                ....
                其它的处理代码
                ....

                // 调用拦截器的前置处理方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                // 调用真正的处理请求的方法
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 找到渲染模版
                applyDefaultViewName(processedRequest, mv);

                // 调用拦截器的后置处理方法
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                ....
                异常处理代码
                ....
        }
        finally {
            ....
            始终要执行的代码
            ....
    }

Spring Mvc对整个请求的处理流程:调用拦截器的前置方法 -> 调用处理请求的方法 -> 渲染模版 -> 调用拦截器的后置处理方法 -> 调用拦截器的完成方法

接下来看一看Spring Mvc是如何实现依此调用这么多拦截器的前置方法, 后置方法, 完成方法的

进入到mapperHandler.applyPreHandle()方法中(调用拦截器的前置方法)

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    // 如果拦截器数组不为空
    if (!ObjectUtils.isEmpty(interceptors)) {
       // 按顺序调用拦截器数组中的preHandle方法
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            // 如果拦截器的preHandle方法返回false, 则调用当前拦截器的triggerAfterCompletion方法, 然后返回, 并且不再调用后续的拦截器
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}

进入到mappedHandler.applyPostHandle()方法中(调用拦截器的后置方法)

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    // 如果拦截器数组不为空
    if (!ObjectUtils.isEmpty(interceptors)) {
        // 倒序调用拦截器数组中拦截器的postHandle方法
        for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }
}

不管是否出异常triggerAfterCompletion方法始终会被调用

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
        throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    // 拦截器数组不为空
    if (!ObjectUtils.isEmpty(interceptors)) {
       // 从成功执行的最后一个拦截器开始逆序调用afterCompletion方法
        for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }
}

看过以上三个方法之后, Spring Mvc如何处理拦截器的前置, 后置, 完成方法就一目了然了. 其实Spring Mvc就是将拦截器统一放到了拦截器数组中, 然后在调用真正的处理请求方法之前和之后正序或者倒序遍历拦截器, 同时调用拦截器的相应的方法. 最后不管是否正常结束这个流程还是出异常都会从成功的最后一个拦截器开始逆序调用afterCompletion方法

总结


分享这篇文章:

上一篇文章
Spring MVC - 请求处理流程
下一篇文章
树的遍历