拦截器和过滤器(原理&区别)

本文详细介绍了SpringMVC中的拦截器和过滤器,包括它们的概念、使用方法、生命周期、执行顺序以及性能优化策略,并对比了两者在底层原理、使用范围、触发时机和执行顺序上的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、拦截器

拦截器是什么

拦截器的使用

拦截器的实现

导入依赖

实现HandlerInterceptor接口

注册拦截器

拦截器的生命周期

拦截器的执行顺序

拦截器的生命周期

多个拦截器的执行流程

拦截器的实际使用

拦截器实现日志记录

实现接口幂等性校验

拦截器的性能优化

二、过滤器

过滤器是什么

过滤器的使用

过滤器的实现

创建过滤器

开启URL过滤

过滤器的生命周期

多个Filter的执行顺序

三、过滤器和拦截器的区别

相同

底层原理

使用范围不同

触发时机不同

控制执行的顺序不同


一、拦截器

拦截器是什么

拦截器(Interceptor)是一种特殊的组件,它是基于反射进行实现。它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截的是servlet 和 controller 之间的请求和响应。

拦截器的使用

  • 权限控制:拦截器可以在请求到达处理器之前进行权限验证,从而实现对不同用户的访问控制。

  • 日志记录:拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试。

  • 接口幂等性校验:拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。

  • 数据校验:拦截器可以在请求到达处理器之前对请求数据进行校验,确保数据的合法性。

  • 缓存处理:拦截器可以在请求处理之后对响应数据进行缓存,提高系统性能。

拦截器的实现

导入依赖

如果是使用的是springboot项目的话,直接导入这个依赖,spring项目需要导入 web 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

实现HandlerInterceptor接口

要在SpringBoot中实现拦截器,首先需要创建一个类并实现HandlerInterceptor接口。HandlerInterceptor接口包含以下三个方法:

preHandle:在请求到达处理器之前执行,可以用于权限验证、数据校验等操作。如果返回true,则继续执行后续操作;如果返回false,则中断请求处理。

postHandle:在处理器处理请求之后执行,可以用于日志记录、缓存处理等操作。

afterCompletion:在视图渲染之后执行,可以用于资源清理等操作。

public class MainInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("我是处理之前!");
        return true;   //只有返回true才会继续,否则直接结束
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("我是处理之后!");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("我是完成之后!");
    }
}

注册拦截器

要让拦截器生效,需要将其注册到InterceptorRegistry中。这可以通过实现WebMvcConfigurer接口并重写addInterceptors方法来实现。注册成功以后,我们还可以设置拦截的规则,拦截的路径,和不拦截的路径

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private MainInterceptor customInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器,并设置拦截路径
        registry.addInterceptor(customInterceptor)
                .addPathPatterns("/**").excludePathPatterns("/exclude/**");
    }
}

拦截器的生命周期

拦截器的执行顺序

当有多个拦截器时,它们的执行顺序取决于注册顺序。先注册的拦截器先执行,后注册的拦截器后执行。在请求处理过程中,拦截器的preHandle方法按注册顺序执行,而postHandle和afterCompletion方法按注册顺序的逆序执行。

拦截器的生命周期

拦截器的生命周期由Spring容器管理。当Spring容器启动时,拦截器会被实例化并初始化;当Spring容器关闭时,拦截器会被销毁。

多个拦截器的执行流程

当有多个拦截器时,它们的执行流程如下:

  1. 执行所有拦截器的preHandle方法,按注册顺序执行。如果某个拦截器的preHandle方法返回false,则中断请求处理,直接执行已执

  2. 拦截器的afterCompletion方法。

  3. 执行处理器的处理方法。

  4. 执行所有拦截器的postHandle方法,按注册顺序的逆序执行。

  5. 渲染视图。

  6. 执行所有拦截器的afterCompletion方法,按注册顺序的逆序执行。

拦截器的实际使用

拦截器实现日志记录

使用拦截器记录接口访问的记录,记录访问该接口的IP地址

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("request.getRemoteHost() = " + request.getRemoteHost());
        System.out.println("request.getParameter(\"id\") = " + request.getParameter("id"));
        return true;   //只有返回true才会继续,否则直接结束
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        request.getParameter("id");
        System.out.println("我是处理之后!");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("我是完成之后!");
    }
}
​

实现接口幂等性校验

拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。以下是一个简单的幂等性校验示例:

public class IdempotentInterceptor implements HandlerInterceptor {
​
    private static final String IDEMPOTENT_TOKEN = "idempotentToken";
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader(IDEMPOTENT_TOKEN);
        if (StringUtils.isEmpty(token)) {
            throw new RuntimeException("Idempotent token is missing");
        }
        if (!checkIdempotentToken(token)) {
            throw new RuntimeException("Duplicate request");
        }
        return true;
    }
​
    private boolean checkIdempotentToken(String token) {
        // Check the token in the cache or database
        // Return true if the token is valid, false otherwise
    }
}
​
//在上述示例中,我们在preHandle方法中检查请求头中的幂等性令牌,如果令牌无效,则抛出异常并中断请求处理。

拦截器的性能优化

拦截器在请求处理过程中可能会影响系统性能,以下是一些性能优化策略:

  • 减少拦截器数量:尽量将相关功能集中到一个拦截器中,避免创建过多的拦截器。

  • 精确配置拦截规则:通过addPathPatterns和excludePathPatterns方法精确配置拦截规则,避免不必要的拦截。

  • 使用异步处理:在拦截器中使用异步处理,避免阻塞请求处理过程。

  • 使用缓存:在拦截器中使用缓存,减少对数据库或其他资源的访问。

二、过滤器

过滤器是什么

过滤器顾名思义就是对事物进行过滤的,在Web中的过滤器,当然就是对请求进行过滤,我们使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。过滤器的实现是通过函数回调进行实现。

函数回调:它指的是将一个函数作为参数传递给另一个函数,并在特定事件发生时被调用执行的函数。这种方式使得在异步编程中,当某个操作完成后,系统能够调用预先定义好的回调函数来处理结果,从而避免阻塞程序的执行,提高程序的效率和响应速度。

过滤器的使用

  • 如登录控制

  • 权限管理

  • 过滤敏感词汇等

过滤器的实现

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

创建过滤器

init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。 FilterConfig可以获取配置信息

doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。

默认就会传入Request和Response,这个参数封装了请求和响应,我们直接使用就行。ServletResquest和ServletResponse可以直接强转成HttpServletRequest和HttpServletResponse,然后使用相应的方法。

destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次

@Component
@WebFilter("/*") // 定义的过滤规则,只过滤对应的url请求
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 后置");
    }
}
开启URL过滤

开启专门的Url过滤需要在启动类添加一个注解 @ServletComponentScan

@SpringBootApplication
@ServletComponentScan
public class InterceptorApplication {
    public static void main(String[] args) {
        SpringApplication.run(InterceptorApplication.class, args);
    }
}

过滤器的生命周期

  1. 初始化阶段(Initialization): 在 Servlet 容器启动时,会实例化所有的过滤器并调用其 init 方法进行初始化。在 init 方法中,过滤器可以进行一些初始化操作,例如读取配置文件、建立数据库连接等。该方法只会在过滤器实例化时调用一次。

  2. 请求处理阶段(Request Processing): 在请求到达 Servlet 前,过滤器可以对请求进行预处理。当请求与过滤器匹配时,Servlet 容器会调用过滤器的 doFilter 方法来处理请求。在 doFilter 方法中,过滤器可以执行一些操作,例如修改请求或响应内容、记录日志、验证权限等。如果请求不符合过滤器的条件,过滤器可以选择放行请求,将请求传递给下一个过滤器或目标 Servlet。

  3. 销毁阶段(Destruction): 在 Servlet 容器关闭时,会销毁所有的过滤器并调用其 destroy 方法进行清理。在 destroy 方法中,过滤器可以进行一些清理操作,例如释放资源、关闭连接等。该方法只会在过滤器销毁时调用一次。

多个Filter的执行顺序
  • 如果我们是在web.xml中配置的过滤器,那么过滤器的执行顺序就是<filter-mapping>在web配置的顺序,配置在上面那么就会先执行。

  • 如果我们是使用@WebFilter进行配置的,那么执行顺序就是字符比较顺序来执行,例如有2个过滤器,一个是AFilter,一个是BFilter,那么AFilter就会先执行。

  • 如果注解和xml混用,那么在web.xml中配置的会先执行。

三、过滤器和拦截器的区别

相同

二者都是体现了AOP的思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的,接下来一一说明。

底层原理

拦截器是使用反射进行实现,过滤器是基于函数回调进行实现

使用范围不同

过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。

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

触发时机不同

过滤器拦截器的触发时机也不同,我们看下边这张图

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

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

控制执行的顺序不同

拦截器:是先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

为什么?

两个方法中在调用拦截器数组 HandlerInterceptor[] 时,循环的顺序竟然是相反的。导致postHandle()preHandle() 方法执行的顺序相反。

&lt;think&gt;好的,用户想了解拦截器过滤器区别以及它们的实际应用案例或实现方法。首先,我需要回顾一下之前提供的引用资料,确保信息准确。根据引用[1]、[3]、[4][5],拦截器过滤器在出身、触发时机、实现方式、项目类型支持使用场景上有区别过滤器Servlet规范的一部分,而拦截器通常由框架如Spring MVC提供。触发时机方面,过滤器在请求进入Servlet之前,拦截器在进入Controller之前。实现上,过滤器基于函数回调,拦截器基于反射或动态代理。 接下来,我需要整理它们的实际应用场景。根据引用[5],过滤器常用于全局处理,比如登录校验、编码处理、敏感词过滤。而拦截器更适用于业务相关的处理,比如权限检查、日志记录、性能监控。例如,在Spring MVC中,拦截器可以预处理请求,记录请求信息,或者验证用户权限。 然后,用户可能想知道具体的实现方法。需要分别给出过滤器拦截器的代码示例。对于过滤器,可以展示一个简单的编码设置过滤器,使用@WebFilter注解。拦截器部分,可以演示如何实现HandlerInterceptor接口,配置拦截路径,并在Spring配置中添加拦截器。 另外,用户可能对它们的区别有更深入的问题,比如为什么拦截器不能修改请求内容,而过滤器可以?这是因为过滤器在更早的阶段处理请求,可以包装Request对象,而拦截器处理的是已经解析过的请求。这部分需要简明扼要地解释。 最后,确保回答结构清晰,分点说明区别应用场景,提供代码示例,并引用相关文献。同时,生成相关问题,帮助用户进一步探索。检查是否有遗漏,比如触发时机的图解,是否需要更详细说明?根据引用[3],触发时机不同,可以提到过滤器Servlet之前,拦截器在Controller前后,但可能不需要深入图解,保持回答简洁。&lt;/think&gt;### 拦截器过滤器区别及实际应用 #### 一、核心区别 1. **出身不同** - **过滤器**:属于 JavaWeb 规范(Servlet API),适用于所有 Web 项目[^5]。 - **拦截器**:由框架(如 Spring MVC)提供,仅适用于支持该框架的项目[^3]。 2. **触发时机** - **过滤器**:在请求进入 Servlet 前执行,响应返回后再次处理。 - **拦截器**:在 Controller 方法执行前后、视图渲染前触发[^4]。 3. **实现方式** - **过滤器**:基于函数回调(`doFilter()` 方法)[^2]。 - **拦截器**:基于反射或动态代理(如 `preHandle()`, `postHandle()`)。 4. **功能范围** - **过滤器**:可修改请求/响应内容(如编码、敏感词替换)。 - **拦截器**:仅读取请求数据,通常用于业务逻辑控制(如权限校验)[^1][^4]。 --- #### 二、实际应用场景 ##### 1. 过滤器(Filter) - **全局编码设置** ```java @WebFilter(&quot;/*&quot;) public class EncodingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { request.setCharacterEncoding(&quot;UTF-8&quot;); response.setCharacterEncoding(&quot;UTF-8&quot;); chain.doFilter(request, response); } } ``` 应用场景:统一处理请求/响应的字符编码[^5]。 - **敏感词过滤** ```java public class SensitiveFilter implements Filter { private List&lt;String&gt; sensitiveWords = Arrays.asList(&quot;test&quot;, &quot;abc&quot;); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String content = request.getParameter(&quot;content&quot;); for (String word : sensitiveWords) { content = content.replaceAll(word, &quot;***&quot;); } chain.doFilter(new CustomRequestWrapper(request, content), response); } } ``` 应用场景:论坛内容或聊天文本的实时过滤。 ##### 2. 拦截器(Interceptor) - **登录权限校验** ```java public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (request.getSession().getAttribute(&quot;user&quot;) == null) { response.sendRedirect(&quot;/login&quot;); return false; // 拦截请求 } return true; } } ``` 应用场景:限制未登录用户访问后台页面。 - **接口耗时统计** ```java public class PerformanceInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { request.setAttribute(&quot;startTime&quot;, System.currentTimeMillis()); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { long duration = System.currentTimeMillis() - (Long) request.getAttribute(&quot;startTime&quot;); System.out.println(&quot;接口耗时:&quot; + duration + &quot;ms&quot;); } } ``` 应用场景:监控 API 性能瓶颈。 --- #### 三、实现方式对比 | 特性 | 过滤器 | 拦截器 | |--------------------|---------------------------------|---------------------------------| | **配置方式** | `web.xml` 或 `@WebFilter` | Spring 配置文件或 `@Configuration` | | **依赖对象** | ServletRequest/Response | HandlerMethod | | **异常处理** | 无法直接捕获 Controller 异常 | 可通过 `afterCompletion` 处理 | --- #### 四、选择建议 - 使用 **过滤器** 的场景: 需要修改请求/响应内容(如压缩数据)、全局安全防护(XSS过滤)。 - 使用 **拦截器** 的场景: 业务级控制(如权限链)、需要获取 Controller 方法信息(如注解解析)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值