过滤器Filter和拦截器Interceptor心得

上一篇文章讲了监听器Listener,下面我们来讲一下过滤器和拦截器。

一、过滤器Filter。

首先,servlet容器(比如tomcat)肯定的要有servlet才能发挥它的光彩。在上古jsp时代,我们会写各种servlet通过不同的请求来实现我们相关的业务,就如同我们现在要写各种controller一样来接收用户请求一样。现在我们不需要写servlet了,因为伟大的spring拯救了我们这些copy工程师,也因为spring的易用的良好生态导致我们很多人现在找不到活。spring给我们弄了个很牛X的servlet,那就是DispatcherServlet。 我们老程序员肯定看过如下配置:

<!-- web.xml -->
<web-app>
  <!-- ... -->
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可选配置:设置Spring MVC配置文件的位置 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
    </init-param>
    <!-- 可选配置:设置DispatcherServlet的加载顺序 -->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <!-- 配置DispatcherServlet处理哪些请求 -->
    <url-pattern>/</url-pattern> <!-- 通常配置为'/'来处理所有请求 -->
  </servlet-mapping>
  <!-- ... -->
</web-app>

 我们Java程序员老是自豪的喊我们写的web是MVC的,大部分依赖于DispatcherServlet它这个家伙。

DispatcherServlet 是 Spring Web MVC 框架的核心组件,它充当了前端控制器的角色,实现了前端控制器设计模式。在基于 Spring MVC 构建的 Java Web 应用程序中,DispatcherServlet 主要承担以下作用:

  1. 统一入口:作为所有HTTP请求的集中处理者,应用程序的所有请求首先都会被路由到DispatcherServlet。

  2. 请求分发:根据用户请求的URL,DispatcherServlet会查找合适的处理器(Controller),这一过程通过HandlerMapping实现,它可以将请求映射到具体的处理器方法。

  3. 处理器适配:找到处理器后,DispatcherServlet使用HandlerAdapter来调用处理器的方法执行业务逻辑,即使处理器有不同的实现方式(如注解控制器、XML配置的控制器等)。

  4. 数据绑定和验证:在调用处理器方法前,DispatcherServlet会自动将请求参数绑定到处理器方法的参数上,并执行任何相关的数据验证。

  5. 视图解析:处理器方法执行完毕后,通常会返回一个逻辑视图名,DispatcherServlet通过ViewResolver接口将逻辑视图名解析成实际的视图对象,进而渲染模型数据并生成最终的响应内容。

  6. 拦截器处理:支持HandlerInterceptor(拦截器)的配置,可以在请求处理前后执行额外的逻辑,例如权限检查、日志记录等。

  7. 异常处理:处理控制器抛出的异常,并根据配置将其映射到特定的错误页面或返回适当的HTTP错误状态码。

  8. 文件上传支持:通过配置MultipartResolver,DispatcherServlet能处理多部分表单提交,如文件上传请求。

  9. 国际化支持:支持对请求进行本地化解析,确保返回的内容符合用户的语言环境。

DispatcherServlet在整个Spring MVC架构中扮演着核心调度和管理角色,它负责协调各个组件以完成HTTP请求的处理并返回响应。通过这种方式,它简化了Web应用程序的设计,促进了模块化和可扩展性。

回归正题,在请求没有到servlet之前或者请求离开了servlet之后,想要干点什么该怎么做呢?其实这个想法在没有spring之前就有古老程序员想到了,比如他们遇到了比如说刚写好了一个销售新研发的薄荷味儿的六味地黄丸的servlet,但是太医院御医突发奇想拍脑门说我要跟踪所有访问过这个servlet的贝勒爷有哪些,但工部侍郎不太想改刚写好的servlet,万一增加了这个功能,明天八旗子弟不愿意了又让去掉。。。

这时候过滤器Filter就登场了,它与servlet非常类似,过滤器就是Java组件,请求发送到servlet之前,可以用过滤器截获和处理请求,另外servlet结束工作之后,但在响应发回给客户之前,可以用过滤器处理响应。过滤器可以链到一起,一个接一个地运行。过滤器设计为完全自包含的。过滤器并不关心在它前面运行了哪些过滤器(如果有的话),也不关心后面还会运行哪个过滤器。

过滤器与servlet很像,过滤器有自己的API。如果一个Java类实现了Filter接口对容器来说就有很大不同,这个类会从原先一个普通的Java类摇身一变而成为一个正式的J2EE过滤器。过滤器API的其他成员允许过滤器访问ServletContext,而且可以与其他过滤器链接。就像servlet一样,过滤器也有一个生命周期。类似于servlet,过滤器有init()和destroy()方法。对应于servlet的doGet()/doPost()方法,过滤器则有一个doFilter()方法。Web应用可以有很多的过滤器,一个给定请求可能导致执行多个过滤器。针对请求要运行哪些过滤器,以及运行的顺序如何,这些都要在DD中声明。

 真正的工作在doFilter()中完成,每次容器认为应该对当前请求应用过滤器时,就会调用doFilter()方法。

doFilterO方法有3个参数:一个ServletRequest(而不是HttpServletRequest),一个ServletResponse(而不是HttpServletResponse),一个FilterChain。

它在古老的定义里是这样的。

那么他就能在servlet执行前后干一些别的事情了。比如:

  1. 自定义Filter: 开发者可以编写自己的Filter实现,例如认证过滤器(Authentication Filter)、授权过滤器(Authorization Filter)、字符编码过滤器(Character Encoding Filter)、日志记录过滤器(Logging Filter)等,用于实现特定的请求预处理和后处理功能。

  2. Spring Security Filter Chain: Spring Security框架提供了一整套安全过滤器链,其中包括多个内置的Filter,如ChannelProcessingFilter(通道处理过滤器)、SecurityContextPersistenceFilter(安全上下文持久化过滤器)、LogoutFilter(注销过滤器)、UsernamePasswordAuthenticationFilter(用户名密码认证过滤器)等,这些过滤器一起协作保障了应用的安全性。

  3. Cross-Origin Resource Sharing (CORS) Filter: 为了支持跨域资源共享,开发者经常编写CORS Filter来处理浏览器的预检请求OPTIONS方法以及设置响应头以允许跨域访问。

  4. GZIP Compression Filter: 用于压缩HTTP响应内容,提升网络传输效率。

  5. Content-Type and Character Encoding Filter: 设置响应的Content-Type和字符编码。

  6. Static Resources Filter: 对静态资源(如CSS、JavaScript、图片等)的访问进行优化处理,如缓存控制。

  7. Locale Change Filter: 用于处理用户地域变更请求,切换应用的语言环境。

在Spring Boot项目中,可以通过实现javax.servlet.Filter接口并配置到Spring的WebApplicationInitializer或通过@WebFilter注解在类上声明的方式创建自定义Filter,并在Spring的配置文件或Java配置类中注册到Servlet容器的过滤器链中。特别是你在学Spring Security的时候会发现有很多filter。

二、拦截器Interceptor。

拦截器(Interceptor)和过滤器(Filter)是Java Web框架中两种不同的设计模式,它们各自独立,并非基于彼此设计,但常常在功能上有协同作用,共同完成对HTTP请求和响应的预处理和后处理工作。

过滤器上面说了,是Servlet规范的一部分,它基于责任链模式,能够对Web容器接收到的所有请求及其对应的响应进行统一处理,例如权限验证、编码转换、压缩等。过滤器按照定义的顺序链式执行,且必须依赖于Servlet容器。

拦截器则是更偏向于具体框架的概念,比如Spring MVC框架中的拦截器,它是基于面向切面编程(AOP)思想设计的,通常利用Java反射机制实现,主要用于处理控制器层(如Action、Controller)的请求和响应。拦截器可以介入到业务逻辑执行前后,进行诸如身份验证、日志记录、性能监控等操作。

虽然拦截器和过滤器都可以实现类似的功能,比如对请求进行预处理和后处理,但它们的设计原理、使用场景以及所在的层次有所不同,在实际项目中,可以根据需求选择使用过滤器、拦截器或同时使用两者来优化应用的功能结构。

在Spring框架中,特别是在Spring MVC环境下,Interceptor(拦截器)主要用于增强或修改请求处理的行为,提供一种灵活的方式来添加通用的横切关注点。比如权限验证、日志记录、性能监控、事务管理等。Spring MVC中主要的Interceptor类型如下:

  1. HandlerInterceptor: 这是最常见的Spring MVC拦截器类型,通过实现org.springframework.web.servlet.HandlerInterceptor接口来创建自定义的拦截器。HandlerInterceptor有三个方法可以覆盖:

    • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):在处理器执行之前调用,可以决定是否继续执行后续的处理流程。
    • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):在处理器执行完并且视图渲染之前调用,可以修改ModelAndView对象。
    • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):在整个请求处理完成后(包括视图渲染)调用,主要用于清理资源等工作。
  2. WebRequestInterceptor: 实现org.springframework.web.context.request.WebRequestInterceptor接口,这种拦截器针对的是所有的Web请求,不仅仅是HTTP请求,它的方法包括:

    • preHandle(WebRequest request):在请求处理之前调用。
    • postHandle(WebRequest request, ModelMap model):在请求处理完成但视图尚未渲染之前调用。
    • afterCompletion(WebRequest request, Exception ex):在请求处理完全结束后调用,无论是否有异常。
  3. AsyncHandlerInterceptor: 用于异步请求处理的拦截器,继承自HandlerInterceptor接口,增加了对异步请求生命周期的支持。

除了上述标准拦截器,Spring还提供了许多与特定功能相结合的拦截器,例如:

  • HandlerExceptionResolver接口及其实现类,用于异常处理的拦截,虽然不是严格意义上的Interceptor,但在请求处理流程中起到类似的作用。

此外,Spring AOP也可以看作是一种广义的拦截器,它可以应用于整个Spring容器中所有bean的方法调用,实现更为广泛的切面编程,但与Spring MVC中的HandlerInterceptor不同,它更多的是面向服务和业务层的增强。

那么拦截器和过滤器怎么选择呢?老祖宗在我们面向对象的同时又给我们搞出来一个面向切面的利器,那就是AOP,比如我们可以实现Spring MVC中的org.springframework.web.servlet.HandlerInterceptor拦截器接口。

该接口定义了三个方法,分别对应请求处理的不同阶段,能让我们直接一砍刀切到我们喜欢的Bean中去 :

  1. preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):在处理器执行前调用,如果该方法返回false,则后续的处理器不会被执行,通常用于权限验证、登录状态检查等预处理操作。

  2. postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):在处理器执行完业务逻辑后,视图渲染前调用,可以修改ModelAndView对象,影响视图呈现的数据。

  3. afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):在整个请求处理完成后(包括视图渲染)调用,用于释放资源、记录日志等收尾工作。

Spring通过HandlerMapping和HandlerExecutionChain维护一系列的拦截器,形成拦截器链(Interceptor Chain),当请求到达DispatcherServlet时,会沿着这个链依次调用每个拦截器的相应方法。

你是不是感觉拦截器和过滤器功能感很像呢,又是pre又是post,对象的前后都能搞而且都带责任链?是不是又觉得拦截器更牛B想搞哪个对象搞哪个对象呢?那我们该怎么选择呢?因为拦截器需要切到Bean中去,如果你明确知道要搞的那些Bean去增强它,那么你可以用拦截器Interceptor。如果你想再请求前后对某些请求统一做操作并不关心后面某某某实现的某些bean,那么你就用Filter。使用范围和应用场景上的不同点:

  1. 实现原理与依赖

    • Filter: 是Java Servlet规范的一部分,基于Servlet容器提供的过滤器接口javax.servlet.Filter实现。Filter的执行由Servlet容器负责调用,对所有进入Servlet容器的HTTP请求均能进行拦截。
    • Interceptor: 是Spring MVC框架提供的功能,基于Spring的org.springframework.web.servlet.HandlerInterceptor接口实现。Interceptor的执行由Spring DispatcherServlet控制,仅对经过Spring MVC框架处理的请求起作用。
  2. 使用范围

    • Filter: 可以应用于任何基于Servlet的Web应用程序,不限于Spring MVC。它可以捕获所有的HTTP请求和响应,包括静态资源请求和其他框架处理的请求。
    • Interceptor: 特别针对Spring MVC框架中的Controller请求处理,无法直接拦截静态资源请求或非Spring MVC处理的请求。
  3. 执行顺序

    • Filter: 执行顺序较早,处于请求到达Spring DispatcherServlet之前,可以对请求进行初步处理,如编码转换、安全检查等。
    • Interceptor: 执行顺序在Filter之后,仅针对Spring MVC Controller的请求处理,发生在Controller方法调用前后以及视图渲染前后。
  4. 功能定位

    • Filter: 一般用于处理跨越多个应用或框架的通用问题,如登录验证、日志记录、编码转换、请求压缩等,这些处理往往不需要知道具体的业务逻辑。
    • Interceptor: 更加贴近业务逻辑,通常用于处理与Controller方法执行紧密相关的任务,如权限验证、性能监控、统一异常处理、事务管理、注入额外数据到ModelAndView等。
  5. 调用时机

    • Filter: 可以在请求之前(doFilter()方法中)、请求之后或整个请求生命周期的任意时刻进行处理。
    • Interceptor: 有明确的三个调用点:preHandle()(前置处理,可决定是否继续执行请求)、postHandle()(后置处理,可以修改模型和视图数据)和afterCompletion()(完全完成,通常用于清理资源)。

总结起来Filter在Web请求处理的早期阶段发挥作用,拥有更广泛的适用范围,而Interceptor则更聚焦于Spring MVC框架内部的请求处理环节,能够深度参与到业务逻辑的执行过程中。两者结合使用,可以为Web应用提供全面且细致的请求处理和响应控制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/564892.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅说深度优先搜索(中)——回溯

写在最前 相信在你们不懈的努力之下&#xff0c;基本的递归一定可以写出来了&#xff0c;那么我们现在就来看看递归的升级版——回溯怎么写吧&#xff01; 简说回溯 递归是一种特别重要的解题策略。大部分题目需要找到最优解&#xff0c;而这个求解过程可能存在一定的规律性…

排序算法之基数排序

目录 一、简介二、代码实现三、应用场景 一、简介 算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度排序方式稳定性基数排序O(n*k)O(n*k)O(n*k)O(nk)Out-place稳定 稳定&#xff1a;如果A原本在B前面&#xff0c;而AB&#xff0c;排序之后A仍然在B的前面&#xff1b…

03-为啥大模型LLM还没能完全替代你?

1 不具备记忆能力的 它是零状态的&#xff0c;我们平常在使用一些大模型产品&#xff0c;尤其在使用他们的API的时候&#xff0c;我们会发现那你和它对话&#xff0c;尤其是多轮对话的时候&#xff0c;经过一些轮次后&#xff0c;这些记忆就消失了&#xff0c;因为它也记不住那…

笔记本电脑坏了硬盘数据会丢失吗 笔记本电脑坏了如何取出硬盘的资料 数据恢复软件

笔记本电脑对我们真的非常重要了&#xff0c;是实现无纸化办公和学习的重要工具&#xff0c;但是如果笔记本电脑坏了我们存储在电脑里的资料该怎么办&#xff1f;笔记本电脑坏了硬盘数据会丢失吗&#xff1f;相信有许多朋友都会有这样的担忧。本文今天就为大家解决笔记本电脑坏…

Docker 的基本管理

一. 云的相关知识 1. 关于云 云端服务器都有哪些提供商&#xff1a; 国内&#xff1a; 阿里云&#xff08;Alibaba Cloud&#xff09;&#xff1a; 提供ECS&#xff08;Elastic Compute Service&#xff09;弹性计算服务&#xff0c;包括通用型、计算型、内存型等多种实例…

CodeGemma初探

什么是 CodeGemma CodeGemma是一系列强大而轻量级的模型的集合&#xff0c;可以执行各种编码任务&#xff0c;包括填充中间代码补全、代码生成、自然语言理解、数学推理和指令跟随。 版本&#xff1a; instruct&#xff1a;7B, 这个版本专门针对自然语言到代码聊天和指令跟随…

【Linux高性能服务器编程】——高性能服务器框架

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之高性能服务器框架介绍&#xff0c;在这篇文章中&#xff0c;你将会学习到高效的创建自己的高性能服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家来理解&…

解锁EDM设计秘籍:关键要素一览,邮件如何设计?

一个成功的EDM邮件需要包含多个关键元素&#xff0c;从内容、设计到呼唤行动&#xff0c;每个环节都至关重要。今天&#xff0c;我们就来探讨EDM邮件中应包含的关键元素&#xff1f;以及如何设计邮件&#xff1f; 一、EDM必备关键要素 1、吸引眼球的主题行 主题行应该简短明了…

NC398 腐烂的苹果

腐烂的苹果 一个腐烂的苹果每分钟可以向上下左右四个方向扩展&#xff0c;扩展之后&#xff0c;又会有新的腐烂的苹果&#xff0c;一直去腐蚀好的苹果&#xff0c;求多少分钟后&#xff0c;网格中全是烂苹果。 第一次做这道题的时候&#xff0c;想到这道题考察的其实是多源BFS…

C#版Facefusion:让你的脸与世界融为一体!-04 人脸替换

C#版Facefusion&#xff1a;让你的脸与世界融为一体&#xff01;-04 人脸替换 目录 说明 效果 模型信息 项目 代码 下载 说明 C#版Facefusion一共有如下5个步骤&#xff1a; 1、使用yoloface_8n.onnx进行人脸检测 2、使用2dfan4.onnx获取人脸关键点 3、使用arcface_w60…

网络基础之-IP地址

文章目录 1. IP地址&#xff1a;网络和主机1.1 A类IP地址1.2 B类IP地址1.3 C类IP地址1.4 D类和E类IP地址 2.几个特殊的IP地址2.1 私有地址2.2网关 1. IP地址&#xff1a;网络和主机 IP地址是用于在计算机网络中唯一标识设备的一组数字。它由32位&#xff08;IPv4&#xff09;或…

05_Flutter屏幕适配

05_Flutter屏幕适配 一.屏幕适配方案 通过指定基准屏宽度&#xff0c;进行适配&#xff0c;基准屏宽度取决于设计图的基准宽度&#xff0c;以iphone 14 pro max为例&#xff0c; devicePixelRatio 物理宽度 / 逻辑宽度(基准宽度) iphone 14 pro max的物理尺寸宽度为1290&…

创新入门|解锁您的潜在市场:探秘付费点击广告(PPC)的秘密武器

在我们的营销领域&#xff0c;按点击付费 &#xff08;PPC&#xff09; 广告是增加流量、提高知名度并最终将点击转化为客户的基石策略。这种有针对性的广告模式&#xff0c;即企业只在点击广告时付费&#xff0c;彻底改变了公司投资在线推广的方式。尽管它看起来很简单&#x…

手写Promise实现

手写Promise实现 一、前言二、代码三、测试四、测试结果 一、前言 阅读参考资料&#xff0c;本文整理出使用 构造函数 手撕出 Promise 的方法&#xff0c;在整理过程中不断添加注解以示思路。有错请指出哟&#xff0c;一起进步&#xff01;&#xff01;&#xff01;class 实现 …

2024接口自动化测试入门基础知识【建议收藏】

接口自动化测试是指通过编写测试脚本和使用相关工具&#xff0c;对软件系统的接口进行自动化测试的过程。 今天本文从4个方面来介绍接口自动化测试入门基础知识 一、接口自动化测试是什么&#xff1f; 二、接口自动化测试流程&#xff1f; 三、接口自动化测试核心知识点有那些…

开始Java之旅

1.Java语言 java是一门优秀的程序设计语言&#xff0c;并且是一种半编译型&#xff0c;半解释型语言。 Java 语言源于 1991 年 4 月&#xff0c;Sun 公司 James Gosling博士 领导的绿色计划(Green Project) 开始启动&#xff0c;此计划最初的目标是开发一种能够在各种消费性电…

Threejs绘制传送带

接下来会做一个MES场景下的数字孪生&#xff0c;所以开始做车间相关的模型&#xff0c;不过还是尽量少用建模&#xff0c;纯代码实现&#xff0c;因为一方面可以动态使用&#xff0c;可以调节长度和宽度等&#xff0c; 下面这节就做一个简单的传送带&#xff0c;这是所有车间都…

学之思考试系统环境启动QA

学之思考试系统环境启动Q&A 目录 学之思考试系统环境启动Q&A后台代码启动失败:前台代码启动失败常见解决方式参考资料后台代码启动失败: 后端代码启动不成功,不能够自动导入maven,配置依赖; 使用idea打开到:\xzs-master\xzs-mysql-master\source\xzs这个路径下;…

函数的创建和调用及删除

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 函数和存储过程非常类似&#xff0c;也是可以存储在 Oracle 数据库中的 PL/SQL代码块&#xff0c;但是有返回值。 可以把经常使用的功能定义为一个函数&#xff0c;就像系统…

使用Flask部署ppocr模型_3

PaddleOCR环境搭建、模型训练、推理、部署全流程&#xff08;Ubuntu系统&#xff09;_1_paddle 多进程推理-CSDN博客 PP-Structure 文档分析-CSDN博客 Pycharm的Terminal进入创建好的虚拟环境 有时候Pycharm的terminal中显示的是硬盘中的项目路径&#xff0c;但没有进入创建好…