天天看点

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

一、实现原理区别

过滤器和拦截器 底层实现方式大不相同:

过滤器

是基于函数回调的,

拦截器

则是基于Java的反射机制(动态代理)实现的。

过滤器(Filter):它依赖于servlet容器。在实现上是基于函数回调。《Java过滤器Filter详解》

在我们自定义的过滤器中都会实现一个

doFilter()

方法,这个方法有一个

FilterChain

参数,而实际上它是一个回调接口。

ApplicationFilterChain

是它的实现类, 这个实现类内部也有一个

doFilter()

方法就是回调方法。

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

每个过滤器Filter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChain的doFilter() 方法,以此循环执行实现函数回调。

拦截器(Interceptor):在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现。

二、使用范围限制

过滤器Filter:过滤器实现的是

javax.servlet.Filter

接口,而这个接口是在

Servlet

规范中定义的,也就是说过滤器

Filter

的使用要依赖于

Tomcat

等容器,导致它只能在

web

程序中使用。

拦截器(Interceptor): 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。在SpringMVC中就是依赖于SpringMVC框架

看看代码实例

我们通过代码来查看触发时机、拦截请求返回,执行顺序等区别

1)第一个过滤器:@Order:表示过滤器执行顺序。通过

@Order

控制过滤器的级别,值越小级别越高越先执行。

Filter使用@WebFilter注解,但注解@WebFilter是Servlet3.0的规范,并不是Spring boot提供的.因此Filter 依赖于Servlet。

package com.demo.springboot2.web.service;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebFilter;
import org.springframework.core.annotation.Order;
import javax.servlet.FilterConfig;
import org.springframework.stereotype.Component;

@Order(1)
@WebFilter(filterName = "DemoFilter1", urlPatterns = "/*")
public class DemoFilter1 implements Filter{

    @Override
    public void destroy() {
        System.out.println("DemoFilter1  is destoried");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("############DemoFilter1 doFilte前############");
        filterChain.doFilter(request, response);
        //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("############DemoFilter1 doFilter后r############");
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        System.out.println("DemoFilter1  is inited");
    }

}           

复制

第二个过滤器:@Order(2):表示过滤器第2个顺序

@Order(2)
@WebFilter(filterName = "DemoFilter2", urlPatterns = "/*")
public class DemoFilter2  implements Filter{
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("############DemoFilter2 doFilte前############");
        filterChain.doFilter(request, response);
        //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("############DemoFilter2 doFilter后############");
    }
}           

复制

第一个拦截器:

package com.demo.springboot2.web.service;

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;

@Component
public class DemoInterceptor1 implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler)throws Exception {
        System.out.println("************DemoInterceptor1 preHandle**********");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
        System.out.println("************DemoInterceptor1 postHandle **********");

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

复制

第二个拦截器:

@Component
public class DemoInterceptor2 implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler)throws Exception {
        System.out.println("************DemoInterceptor2 preHandle**********");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
        System.out.println("************DemoInterceptor2 postHandle **********");

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

复制

注册拦截器:

package com.demo.springboot2.web.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfigurer implements WebMvcConfigurer {

    @Autowired
    private DemoInterceptor1 demoInterceptor1;

    @Autowired
    private DemoInterceptor2 demoInterceptor2;

    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }

    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns("/**") 表示拦截所有的请求,
        // excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
        registry.addInterceptor(demoInterceptor1).addPathPatterns("/**").excludePathPatterns("/login", "/register");
        registry.addInterceptor(demoInterceptor2).addPathPatterns("/**").excludePathPatterns("/login", "/register");
        System.out.println("************addInterceptors**********");
    }
}           

复制

controller代码:

package com.demo.springboot2.web.controller;

import org.springframework.web.bind.annotation.*;

@RestController
public class TestController {
    @RequestMapping("/")
    @ResponseBody
    public String index() {
        return "Hello world";
    }
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        System.out.println("------------TestController-----------");
        return "Hello test";
    }
}           

复制

请求:http://localhost:9091/test

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

三、触发时机

滤器Filter是在请求进入web容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

执行顺序 :Filter 处理 -> Interceptor前置 -> controller -> Interceptor处理中 -> Interceptor 处理后 -Filter 处理后

四、拦截请求的范围不同

求资源:http://localhost:9091/favicon.ico

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

只有两个过滤器

Filter

执行:

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对

Controller

中请求或访问

static

目录下的资源请求起作用。

五、执行顺序

http://localhost:9091/test

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解 一、实现原理区别二、使用范围限制看看代码实例三、触发时机四、拦截请求的范围不同五、执行顺序

1、多个过滤器的执行顺序跟定义的先后关系有关。

通过@Order控制过滤器的级别,值越小级别越高越先执行。

2、多个拦截器执行顺序跟注册先后顺序有关。

registry.addInterceptor(demoInterceptor1).addPathPatterns("/**").excludePathPatterns("/login", "/register");

registry.addInterceptor(demoInterceptor2).addPathPatterns("/**").excludePathPatterns("/login", "/register");

拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。

3、先声明的拦截器

preHandle()

方法先执行,而

postHandle()

方法反而会后执行。

postHandle()

方法被调用的顺序跟

preHandle()

居然是相反的。那为什么会这样?

我们要知道

controller

中所有的请求都要经过核心组件

DispatcherServlet

路由,都会执行它的

doDispatch()

方法,而拦截器

postHandle()

preHandle()

方法便是在其中调用的。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
        try {
         ...........
            try {
           
                // 获取可以执行当前Handler的适配器
                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;
                    }
                }
                // 注意: 执行Interceptor中PreHandle()方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 注意:执行Handle【包括我们的业务逻辑,当抛出异常时会被Try、catch到】
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                applyDefaultViewName(processedRequest, mv);

                // 注意:执行Interceptor中PostHandle 方法【抛出异常时无法执行】
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
        }
        ...........
    }           

复制

看看两个方法

applyPreHandle()

applyPostHandle()

具体是如何被调用的,就明白为什么

postHandle()

preHandle()

执行顺序是相反的了。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if(!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }           

复制

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(!ObjectUtils.isEmpty(interceptors)) {
            for(int i = interceptors.length - 1; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }           

复制

发现两个方法中在调用拦截器数组

HandlerInterceptor[]

时,循环的顺序竟然是相反的。。。,导致

postHandle()

preHandle()

方法执行的顺序相反。