过滤器与拦截器的N个区别,别傻傻分不清了

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 过滤器与拦截器的N个区别,别傻傻分不清了

Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。但它们之间又有不少区别,很多朋友工作多年,可能还没有深刻的了解它们的具体使用以及它们之间的区别。本文带大家全面了解一下它们的使用、实现机制以及区别。


过滤器(Filter)的详解及使用

过滤器(Filter)属于Servlet的范畴,可以认为是Servlet的一种“加强版”,通过实现javax.servlet.Filter接口来实现功能。主要用于对用户请求进行预处理,是个典型的处理链。通常使用场景:检查用户授权、记录日志信息、解码、过滤字符编码等。


基本工作原理:配置完过滤器及需要拦截的请求,当请求到来时,通过过滤器提供的方法可以对请求或响应(Request、Response)统一处理。比如,可判断用户是否登录,是否拥有请求的访问权限等。在Web应用启动时,过滤器仅会被初始化一次,便可处理后续请求,只有Web应用停止或重新部署时才能销毁。


使用Filter完整的流程是:Filter对用户请求进行“预处理”,接着将请求交给Servlet进处理并生成响应,最后Filter再对服务器响应进行“后处理”。


上述流程具体到类的处理就是:1、Filter在ServletRequest到达Servlet之前,拦截客户的ServletRequest;2、根据需要检查ServletRequest,也可修改ServletRequest头和数据;3、在ServletResponse到达客户端之前,拦截ServletResponse;4、根据需要检查HttpServletResponse,也可修改HttpServletResponse头和数据。


创建Filter必须实现javax.servlet.Filter接口,该接口定义了3个方法:


void init(FilterConfig filterConfig):容器启动初始化Filter时会被调用,整个生命周期只会被调用一次。可用于完成Filter的初始化。

void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 实现过滤功能,就是通过该方法对每个请求增加额外的处理。通过其参数FilterChain调用下一个过滤器。

void destroy():用于Filter销毁前,完成某些资源的回收。

其中,doFilter方法便是实现对用户请求进行预处理(ServletRequest request)和对服务器响应进行后处理(ServletResponse response)的方法。预处理和后处理的分界线为是否调用了chain.doFilter()。在执行该方法之前,是对用户请求进行预处理,在执行该方法之后,是对服务器响应进行后处理。


下面以具体的实现代码来展示一下:


public class LogFilter 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);
        System.out.println("Filter 后处理");
    }
    @Override
    public void destroy() {
        System.out.println("容器销毁");
    }
}

关于Filter的使用在普通的Web项目中可在web.xml中配置:

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.secbro2.learn.filter.LogFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

如果是SpringBoot项目,首先使用@Component将LogFilter实例化,然后通过如下配置文件,进行具体的配置:

@Configuration
public class FilterConfig {
    @Resource
    private LogFilter logFilter;
    @Bean
    public FilterRegistrationBean<Filter> registerAuthFilter() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(logFilter);
        registration.addUrlPatterns("/*");
        registration.setName("authFilter");
        // 值越小,Filter越靠前
        registration.setOrder(1);
        return registration;
    }
}

定义一个Contoller,然后依次执行启动项目、访问Controller、关闭项目,打印的日志信息依次为:

Filter 初始化
---以上为启动项目时打印---
Filter 预处理
Controller中处理业务逻辑
Filter 后处理
---以上为访问Controller时打印---
容器销毁
---以上为关闭服务时打印---

拦截器(Interceptor)的详解及使用

拦截器,在AOP(Aspect-Oriented Programming)中用于某个方法或字段被访问之前进行拦截,然后在其之前或之后加入某些操作。拦截器作为动态拦截Action调用的对象,它提供了一种机制使开发者可以在Action执行前后定义可执行的代码,也可以在Action执行前阻止其执行。


拦截器将Action共用的行为独立出来,在Action执行前后执行。常见的应用场景比如权限管理、日志服务等。


在Spring MVC当中要使用拦截器需要实现org.springframework.web.servlet.HandlerInterceptor接口,该接口定义了如下三个方法:


(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,会在请求处理之前被调用。SpringMVC中的Interceptor是链式调用的,可以存在多个Interceptor。Interceptor的调用会依据声明顺序依次执行,最先执行的都是preHandle方法,可在该方法中进行一些前置(预)处理,也可进行判断来决定是否要继续执行。当返回为false 时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时,会继续调用下一个Interceptor的preHandle方法,执行完最后一个Interceptor后会调用当前请求的Controller方法。


(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,会在Controller方法调用之后,DispatcherServlet进行渲染视图之前被调用,所以可以对Controller处理之后的ModelAndView对象进行操作。postHandle方法被调用的方向跟preHandle是相反的,先声明的Interceptor的postHandle方法反而会后执行。


(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,会在整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要是用于进行资源清理。


来看一个具体的示例:


@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("Interceptor preHandle");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("Interceptor postHandle");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("Interceptor afterCompletion");
    }
}

对应LoginInterceptor需添加到Spring MVC当中:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Resource
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
    }
}

这里拦截所有的请求,执行对应的Controller之后,会看到打印如下信息:

Interceptor preHandle
Controller中处理业务逻辑
Interceptor postHandle
Interceptor afterCompletion

很明显可以看到,当一个请求过来之后,会先后执行preHandle方法、Controller中的业务、postHandle方法和afterCompletion方法。


过滤器与拦截器的区别

经过上面的学习,我们已经大概了解了过滤器和拦截器的基本使用和功能,想必已经感觉到它们之间的一些区别了。先看一张图,可以更加明显的看出过滤器和拦截器在使用过程中所处的位置和使用的时机。


image.png汇总一下就是:


1、使用范围与规范不同:Filter是Servlet规范中定义的,只能用于Web程序中,依赖于Servlet容器。拦截器是Spring的组件,可用于Web程序、Application、Swing等程序,不依赖Servlet容器。


2、使用资源不同:拦截器可以使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IOC注入到拦截器即可;而Filter则不能。


3、作用范围不同:Filter在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后,对Action请求其作用,可以访问Action上下文、值栈里的对象等,具有更大的弹性。因此,在Spring框架的过程中,要优先使用拦截器。而滤器则可以对几乎所有的请求起作用。


4、实现机制不同:拦截器是基于java的反射机制的,而过滤器是基于函数回调。


上面介绍了过滤器和拦截器的基本不同之处,这里再对上面的图进一步细化,可得到下图:


image.png通过上图,我们可以进一步看到拦截器和过滤器的方法在整个请求过程中所处的位置。


小结

通过上面的学习,想必大家已经掌握了过滤器和拦截器的基本使用。最后补充一下,什么时候适合使用过滤器,什么时候又适合使用拦截器呢?当需要过滤掉其中的部分信息,只留一部分时,就用过滤器;当需要对其流程进行更改,做相关的记录时用拦截器。


最近个人视频号“二师兄…”也开始持续更新了,一分钟为大家讲解一个知识点,最近正在持续更新JVM相关内容。有些知识你并不是不会,可能只是不知道。视频号“二师兄…”就是为大家解决不知道的问题,要想来年拿到好offer,知道一些别人不知道的硬核知识,就关注一下吧。



相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
|
10天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
9天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
401 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
3天前
|
存储 安全 前端开发
如何将加密和解密函数应用到实际项目中?
如何将加密和解密函数应用到实际项目中?
197 138
|
9天前
|
人工智能 Java API
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
本文介绍AI大模型的核心概念、分类及开发者学习路径,重点讲解如何选择与接入大模型。项目基于Spring Boot,使用阿里云灵积模型(Qwen-Plus),对比SDK、HTTP、Spring AI和LangChain4j四种接入方式,助力开发者高效构建AI应用。
376 122
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
|
3天前
|
存储 JSON 安全
加密和解密函数的具体实现代码
加密和解密函数的具体实现代码
195 136
|
21天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1347 8
|
8天前
|
监控 JavaScript Java
基于大模型技术的反欺诈知识问答系统
随着互联网与金融科技发展,网络欺诈频发,构建高效反欺诈平台成为迫切需求。本文基于Java、Vue.js、Spring Boot与MySQL技术,设计实现集欺诈识别、宣传教育、用户互动于一体的反欺诈系统,提升公众防范意识,助力企业合规与用户权益保护。
|
20天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
1454 87