前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解

Spring学习笔记(8)--拦截器Interceptor和过滤器Filter的区别详解

作者头像
黄规速
发布2022-04-14 16:07:29
1.5K0
发布2022-04-14 16:07:29
举报

一、实现原理区别

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

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

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

每个过滤器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。

代码语言:javascript
复制
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个顺序

代码语言:javascript
复制
@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后############");
    }
}

第一个拦截器:

代码语言:javascript
复制
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**********");
    }
}

第二个拦截器:

代码语言:javascript
复制
@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**********");
    }
}

注册拦截器:

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
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

三、触发时机

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

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

四、拦截请求的范围不同

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

只有两个过滤器Filter执行:

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

五、执行顺序

http://localhost:9091/test

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()方法便是在其中调用的。

代码语言:javascript
复制
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() 执行顺序是相反的了。

代码语言:javascript
复制
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;
    }
代码语言:javascript
复制
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() 方法执行的顺序相反。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-03-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、实现原理区别
  • 二、使用范围限制
  • 看看代码实例
  • 三、触发时机
  • 四、拦截请求的范围不同
  • 五、执行顺序
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档