75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?

这篇具有很好参考价值的文章主要介绍了75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?

区别

  • 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  • 拦截器只能对action请求(DispatcherServlet 映射的请求)起作用,而过滤器则可以对几乎所有的请求起作用。
  • 拦截器可以访问容器中的Bean(DI),而过滤器不能访问(基于spring注册的过滤器也可以访问容器中的bean)。

执行顺序

过滤器 和 拦截器的触发时机也不同,我们看下边这张图。
75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

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

知识延伸

过滤器 (Filter)

过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。

  • init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。「注意」:这个方法必须执行成功,否则过滤器会不起作用。
  • doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
  • destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次。

使用过滤器的代码

@Component
public class MyFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        System.out.println("Filter 前置");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter 处理中");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

        System.out.println("Filter 后置");
    }
}



拦截器 (Interceptor)

拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。

首先编写一个简单的拦截器处理类,请求的拦截是通过HandlerInterceptor 来实现,看到HandlerInterceptor 接口中也定义了三个方法。

  • preHandle() :这个方法将在请求处理之前进行调用。「注意」:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
  • postHandle():只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 「有意思的是」:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
  • afterCompletion():只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。

使用拦截器的代码

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("Interceptor 前置");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("Interceptor 处理中");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        System.out.println("Interceptor 后置");
    }
}

将自定义好的拦截器处理类进行注册,并通过addPathPatterns、excludePathPatterns等属性设置需要拦截或需要排除的 URL。

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
         //  /**表示拦截所有的路径
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
    }
}

主要区别

(1)拦截器是基于java的反射机制的,而过滤器是基于函数回调。

(2)拦截器不依赖于servlet容器,而过滤器依赖于servlet容器。

(3)拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

(4)拦截器可以访问action上下文、值栈里的对象,而过滤器不能。

(5)在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

拦截器和过滤器的区别

实现原理不同

过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的。

这里重点说下过滤器!
在我们自定义的过滤器中都会实现一个 doFilter()方法,这个方法有一个FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。

public interface FilterChain {

    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud
ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。

public final class ApplicationFilterChain implements FilterChain {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
            ...//省略
            internalDoFilter(request,response);
    }
 
    private void internalDoFilter(ServletRequest request, ServletResponse response){
    if (pos < n) {
            //获取第pos个filter    
            ApplicationFilterConfig filterConfig = filters[pos++];        
            Filter filter = filterConfig.getFilter();
            ...
            filter.doFilter(request, response, this);
        }
    }
 
}


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

 @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);
    }

使用范围不同

我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。
75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud
而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。
75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud

拦截的请求范围不同

在上边我们已经同时配置了过滤器和拦截器,再建一个Controller接收请求测试一下。

@Controller
@RequestMapping()
public class TestController {

    @RequestMapping("/test1")
    @ResponseBody
    public String test1(String a) {
        System.out.println("我是controller");
        return null;
    }
}


项目启动过程中发现,过滤器的init()方法,随着容器的启动进行了初始化。
75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud
此时浏览器发送请求,F12 看到居然有两个请求,一个是我们自定义的 Controller 请求,另一个是访问静态图标资源的请求。
75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud
看到控制台的打印日志如下:

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

过滤器Filter执行了两次,拦截器Interceptor只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。

注入Bean情况不同

在实际的业务场景中,应用到过滤器或拦截器,为处理业务逻辑难免会引入一些service服务。

下边我们分别在过滤器和拦截器中都注入service,看看有什么不同?

@Component
public class TestServiceImpl implements TestService {

    @Override
    public void a() {
        System.out.println("我是方法A");
    }
}


过滤器中注入service,发起请求测试一下 ,日志正常打印出“我是方法A”。

@Autowired
private TestService testService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter 处理中");
        testService.a();
        filterChain.doFilter(servletRequest, servletResponse);
 }

Filter 处理中
我是方法A
Interceptor 前置
我是controller
Interceptor 处理中
Interceptor 后置

在拦截器中注入service,发起请求测试一下 ,竟然报错了,debug跟一下发现注入的service怎么是Null啊?
75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?,Spring最新全家桶面试1000道必考题,spring,java,后端,springmvc,ssm,spring boot,spring cloud
这是因为加载顺序导致的问题,拦截器加载的时间点在springcontext之前,而Bean又是由spring进行管理。

解决方案也很简单,我们在注册拦截器之前,先将Interceptor 手动进行注入。「注意」:在registry.addInterceptor()注册的是getMyInterceptor() 实例。

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor getMyInterceptor(){
        System.out.println("注入了MyInterceptor");
        return new MyInterceptor();
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
    }
}

控制执行顺序不同

实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。

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

@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyFilter2 implements Filter {

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

 @Override
 public void addInterceptors(InterceptorRegistry registry) {
 
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1);
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3);
        
 }

看到输出结果发现,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

postHandle() 方法被调用的顺序跟 preHandle() 居然是相反的!如果实际开发中严格要求执行顺序,那就需要特别注意这一点。

Interceptor1 前置
Interceptor2 前置
Interceptor 前置
我是controller
Interceptor 处理中
Interceptor2 处理中
Interceptor1 处理中
Interceptor 后置
Interceptor2 处理后
Interceptor1 处理后

「那为什么会这样呢?」 得到答案就只能看源码了,我们要知道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() 方法执行的顺序相反。文章来源地址https://www.toymoban.com/news/detail-835057.html

到了这里,关于75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SpringBoot】过滤器,监听器,拦截器介绍

    通过两幅图我们可以理解拦截器和过滤器的特点 1、过滤器 过滤器是在请求进入tomcat容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。 理解上面这句话我们就可以知道,进入servlet之前,主要是两个参数:ServletRequest,

    2024年02月04日
    浏览(55)
  • 过滤器,监听器与拦截器的区别

    ​ 过滤器和监听器不是Spring MVC中的组件,而是Servlet的组件,由Servlet容器来管理。拦截器是Spring MVC中的组件,由Spring容器来管理 ​ Servlet过滤器与Spring MVC 拦截器在Web应用中所处的层次如下图所示: 过滤器是Servlet的高级特性之一,是实现Filter接口的Java类。其基本功能就是对

    2024年02月14日
    浏览(47)
  • spring boot 过滤器&拦截器与aop

    在使用 Spring 框架时,可以通过在 web.xml 配置文件中注册过滤器,使其在请求进入 Spring 前就能够进行预处理。这样可以在请求进入 Spring MVC 的 DispatcherServlet 之前,对请求进行拦截、修改或者过滤。 过滤器在 Spring 中的应用场景包括但不限于: 字符编码过滤:通过过滤器,在

    2024年02月01日
    浏览(47)
  • Springboot中使用拦截器、过滤器、监听器

    Javaweb三大组件:servlet、Filter(过滤器)、 Listener(监听器) SpringBoot特有组件:Interceptor(拦截器) 过滤器、拦截器、监听器、AOP(后续文章介绍)、全局异常处理器(后续文章介绍)是搭建系统框架时,经常用到的部分,全局异常处理器的作用很明显,就是处理接口执行

    2024年02月03日
    浏览(40)
  • 登录页面jwt密钥,过滤器,拦截器,异常处理

    需求: 用户未登录时,访问其他也没面,操作添加、删除等操作时,强行跳转至登录页面。 实现方法: 1.使用Cookie,登录后后端添加一个cookie,每次页面判断是否有cookie, 2。使用session,原理同上,只不过session是存储在服务器里的,cookie是在浏览器里。 3。使用jwt令牌,登

    2023年04月25日
    浏览(52)
  • Spring Boot拦截器与过滤器的区别

    在使用Spring Boot开发Web应用程序时,您可能需要在处理请求之前或之后执行某些操作。这些操作可以包括身份验证、日志记录、性能监测等。在这种情况下,您可以使用两种不同的机制:拦截器和过滤器。本文将介绍这两种机制及其区别,并提供一些示例代码来演示如何在S

    2024年02月08日
    浏览(55)
  • SpringBoot2.0(过滤器,监听器,拦截器)

    使用Servlet3.0的注解进行配置 启动类里面增加 @ServletComponentScan ,进行扫描 新建一个Filter类,implements Filter ,并实现对应接口 @WebFilter 标记一个类为Filter,被spring进行扫描 urlPatterns:拦截规则,支持正则 控制chain.doFilter的方法的调用,来实现是否通过放行, 不放行的话,web应用

    2024年02月07日
    浏览(40)
  • 过滤器(Filter)和拦截器(Interceptor)有什么不同?

    过滤器(Filter)和拦截器(Interceptor)是用于处理请求和响应的中间件组件,但它们在实现方式和应用场景上有一些不同。 过滤器 是Servlet规范中定义的一种组件,通常以Java类的形式实现。过滤器通过在 web.xml 配置文件中声明来注册,并在Web应用程序的请求和响应链中拦截请

    2024年02月07日
    浏览(36)
  • springbboot拦截器,过滤器,监听器及执行流程

    过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前 请求按照上图进入conteoller后执行完再返回 过滤器是Servlet规范中定义的一种组件,可以用于在请求进入Web应用程序之前或响应离开Web应用程序之前对请

    2024年02月13日
    浏览(39)
  • Spring中的拦截器与过滤器:原理、区别与案例解析

    前言 在Web应用中,我们经常需要对用户的请求进行某种处理,比如权限验证、日志记录等。 Spring框架提供了两种机制来实现这一需求:拦截器和过滤器。虽然它们的目标相似,但在使用上存在一些差异。本篇文章我们将详细探讨这两种机制的原理、区别,希望能给各位大佬

    2024年04月23日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包