Java过滤器与拦截器的区别

article/2025/9/12 6:19:20

Java过滤器与拦截器的区别

  • 1. 过滤器与拦截器概述
    • 1.1 过滤器 Filter
    • 1.2 拦截器 interceptor
  • 2. 过滤器与拦截器区别
  • 3. 过滤器与拦截器实现
    • 3.1 过滤器(Filter)
    • 3.2 拦截器 (Interceptor)
    • 3.3 拦截器WebMvc配置
    • 3.4 切片(Aspect)
  • 4. 过滤器与拦截器执行顺序验证

1. 过滤器与拦截器概述

1.1 过滤器 Filter

依赖于servlet容器,实现基于函数回调,可以对几乎所有请求进行过滤,
但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,
获取我们想要获取的数据,过滤器一般用于登录权限验证、资源访问权限控制、敏感词汇过滤、
字符编码转换等等操作,便于代码重用,不必每个servlet中进行冗余操作。Java中的Filter并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 
主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。
完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,
最后Filter再对服务器响应进行后处理。在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest。
根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。在响应到达客户端之前,拦截HttpServletResponse。根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据。Filter随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。
1.启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
2.每一次请求时都只调用方法doFilter()进行处理;
3.停止服务器时调用destroy()方法,销毁实例。

1.2 拦截器 interceptor

依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。
在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。
由于拦截器是基于web框架的调用,拦截器可以调用IOC容器中的各种依赖,而过滤器不能,
因此可以使用Spring的依赖注入进行一些业务操作,
同时一个拦截器实例在一个controller生命周期之内可以多次调用。
但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。spring mvc中的Interceptor可以理解为是Spring MVC框架对AOP的一种实现方式。
一般简单的功能又是通用的,每个请求都要去处理的,比如判断token是否失效可以使用spring mvc的HanlderInterceptor, 
复杂的,比如缓存,需要高度自定义的就用spring aop。一般来说service层更多用spring aop,
controller层有必要用到request和response的时候,可以用拦截器。spring mvc中的Interceptor拦截请求是通过HandlerInterceptor来实现的。
所以HandlerInteceptor拦截器只有在Spring Web MVC环境下才能使用。
在SpringMVC中定义一个拦截器主要有两种方式,第一种方式是要实现Spring的HandlerInterceptor接口,
或者是其它实现了HandlerInterceptor接口的类,比如HandlerInterceptorAdapter。
第二种方式是实现WebRequestInterceptor接口,或者其它实现了WebRequestInterceptor的类。HandlerInterceptor接口定义方法preHandle, postHandle, 和afterCompletion:
preHandle(进入 Handler方法之前执行):预处理回调方法,实现处理器的预处理(如登录检查),返回值:true表示继续流程
(如调用下一个拦截器或处理器),false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,
此时我们需要通过response来产生响应。
postHandle(进入handler方法之后,返回modelAndView之前):后处理回调方法,
实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView
(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。afterCompletion(执行Handler完成执行此方法):整个请求处理完毕回调方法,即在视图渲染完毕时回调。
该方法也是需要当前对应的Interceptor 的preHandle方法的返回值为true时才会执行。
这个方法的主要作用是用于进行资源清理工作的,如性能监控中我们可以在此记录结束时间并输出消耗时间。

2. 过滤器与拦截器区别

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

2、使用范围不同
我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。
而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。

3、触发时机不同
过滤器 和 拦截器的触发时机也不同,我们看下边这张图。
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
在这里插入图片描述
4、拦截的请求范围不同
过滤器的init()方法,随着容器的启动进行了初始化。
执行顺序 :Filter 处理中 -> Interceptor 前置 -> 我是controller -> Interceptor 处理中 -> Interceptor 处理后
过滤器Filter执行了两次,拦截器Interceptor只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。

5、注入Bean情况不同
在拦截器与过滤器中分别注入service进行逻辑处理,拦截器会空指针:
原因:拦截器加载的时间点在springcontext之前,而Bean又是由spring进行管理。
解决方案也很简单,我们在注册拦截器之前,先将Interceptor 手动进行注入。「注意」:在registry.addInterceptor()注册的是getMyInterceptor() 实例。

6、控制执行顺序不同
实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。
过滤器用@Order注解控制执行顺序,通过@Order控制过滤器的级别,值越小级别越高越先执行。
拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。

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

原因:
得到答案就只能看源码了,我们要知道controller 中所有的请求都要经过核心组件DispatcherServlet路由,都会执行它的 doDispatch() 方法,而拦截器postHandle()、preHandle()方法便是在其中调用的。
看看两个方法applyPreHandle()、applyPostHandle()具体是如何被调用的,就明白为什么postHandle()、preHandle() 执行顺序是相反的了。
发现两个方法中在调用拦截器数组 HandlerInterceptor[] 时,循环的顺序竟然是相反的。。。,导致postHandle()、preHandle() 方法执行的顺序相反。

3. 过滤器与拦截器实现

3.1 过滤器(Filter)

package com.example.category.aop;import lombok.extern.slf4j.Slf4j;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;/*** 过滤器(Filter)* 注意:* init与destroy方法不是所必须实现的,可以选择或者手动实现* 启动类如果不加@ServletComponentScan就算这个类和运行类平级或者子级也不会被扫描上* filterName可以省略不能重复@WebFilter(filterName = "myFilterTwo"),** @author zrj* @since 2021/8/17**/
@Slf4j
@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("[过滤器]过滤器初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("[过滤器]过滤器执行过滤操作");//放行filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {log.info("[过滤器]过滤器销毁");}
}

3.2 拦截器 (Interceptor)

package com.example.category.aop;import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 拦截器 (Interceptor)* HandlerInterceptor:springMVC框架拦截器* 必须在WebMvc配置以后生效** @author zrj* @since 2021/8/17**/
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {/*** 前置拦截器* 如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("[拦截器]前置拦截器preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {log.info("[拦截器]处理拦截器postHandle");}/*** 后置拦截器* afterCompletion:只有在 preHandle() 方法返回值为true 时才会执行*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {log.info("[拦截器]后置拦截器afterCompletion");}
}

3.3 拦截器WebMvc配置

package com.example.category.config;import com.example.category.aop.MyInterceptor;
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.WebMvcConfigurer;import javax.annotation.Resource;/*** WebMvc配置* 配置自定义拦截器** @author zrj* @since 2021/8/17**/
@Configuration
public class CommonWebConfig implements WebMvcConfigurer {@Resourceprivate MyInterceptor myInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myInterceptor);}}

3.4 切片(Aspect)

package com.example.category.aop;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** 切片(Aspect)** @author zrj* @since 2021/8/17**/
@Slf4j
@Aspect
@Component
public class MyAspect {// 定义切点位置private final String POINT_CUT = "execution(* com.example.category.controller..*.*(..))";private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();/*** 定义切点位置:下面如果你在SSM中用AOP,在xml中配的就是下面*/@Pointcut(POINT_CUT)public void pointCut() {}/*** 前置通知** @param joinPoint 连接点* @throws Throwable*/@Before("pointCut()")public void before(JoinPoint joinPoint) throws Throwable {log.info("[切片]前置通知@Before");//获取目标方法参数信息Object[] args = joinPoint.getArgs();Arrays.stream(args).forEach(arg -> {try {log.info(OBJECT_MAPPER.writeValueAsString(arg));} catch (JsonProcessingException e) {log.info(arg.toString());}});}/*** 后置返回通知* 如果第一个参数为JoinPoint,则第二个参数为返回值的信息* 如果第一个参数不为JoinPoint,则第一个参数为returning中对应的参数* returning:限定了只有目标方法返回值与通知方法参数类型匹配时才能执行后置返回通知,否则不执行,* 参数为Object类型将匹配任何目标返回值*/@AfterReturning(value = POINT_CUT, returning = "result")public void doAfterReturningAdvice1(JoinPoint joinPoint, Object result) {log.info("[切片]后置返回通知@AfterReturning,第一个的返回值:" + result);}@AfterReturning(value = POINT_CUT, returning = "result", argNames = "result")public void doAfterReturningAdvice2(String result) {log.info("[切片]后置返回通知@AfterReturning,第二个的返回值:" + result);}/*** 后置异常通知* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;* throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,* 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。** @param joinPoint 连接点* @param exception 异常*/@AfterThrowing(value = POINT_CUT, throwing = "exception")public void doAfterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {log.info("[切片]后置异常通知@AfterThrowing");log.info(joinPoint.getSignature().getName());if (exception instanceof NullPointerException) {log.info("发生了空指针异常!!!!!");}}/*** 后置通知** @param joinPoint 连接点* @return void*/@After(value = POINT_CUT)public void doAfterAdvice(JoinPoint joinPoint) {log.info("[切片]后置通知@After");}/*** 环绕通知:* 注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型*/@Around(value = POINT_CUT)public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {log.info("[切片]环绕通知@Around开始," + proceedingJoinPoint.getSignature().toString());Object obj = null;try {obj = proceedingJoinPoint.proceed(); //可以加参数log.info("[切片]环绕通知@Around执行" + obj.toString());} catch (Throwable throwable) {throwable.printStackTrace();}log.info("[切片]环绕通知@Around结束");return obj;}}

4. 过滤器与拦截器执行顺序验证

-> 过滤器doFilter -> 前置拦截器preHandle
-> 环绕通知@Around开始 -> 前置通知@Before
-> UserController
-> 后置返回通知@AfterReturning -> 后置通知@After
-> 环绕通知@Around执行 ->环绕通知@Around结束
-> 处理拦截器postHandle -> 后置拦截器afterCompletion

在这里插入图片描述


http://chatgpt.dhexx.cn/article/9AwkDoew.shtml

相关文章

C语言pow函数的调用

1.pow函数的含义&#xff1a;在math.h的头文件中是指a的b次方。 2.对于pow函数的直接应用&#xff1a; #include<stdio.h> #include<math.h> //***math头文件 int main() {double a2,b3;double result;resultpow(2,3); …

mysql pow函数怎么用_pow函数怎么用

PHP pow函数表示指数表达式。 pow函数怎么用&#xff1f; php pow()函数 语法 作用&#xff1a;pow()函数的作用是将一个数进行n次方计算后返回 语法&#xff1a;pow(X,Y); 参数&#xff1a; X表示要做处理的数字 Y表示指定n次方中的n数值 说明&#xff1a;返回X的Y次方幂&…

c语言的pow函数运用

在c语言当中我们要计算一个数的n次方时候&#xff0c;可以使用多种方法&#xff0c;但是也有一种比较简单的方法&#xff0c;便是调用一个函数pow函数。 pow函数在使用时候需要引用头文件#include<math.h>&#xff0c;接下来可以引用一个例子&#xff1a; 如果现在假设我…

C语言中的pow函数

在我们学习C语言时&#xff0c;在一些算法的问题上常常会遇到让我们求一个数的n次方&#xff0c;这时候为了方便我们可以使用C语言函数库给我们的pow函数&#xff0c;*因为这是从C语言函数库调用出来的所以我们在使用前需要引入头文件#include<math.h>。 比如当我们输入…

pow函数python_python pow函数怎么用

python中的pow函数的功能是计算x的y次幂。本篇文章将带大家一起了解一下&#xff0c;pow()函数在Python中的用法。感兴趣的朋友了解一下。 以下是 math 模块 pow() 方法的语法:import math math.pow( x, y ) 内置的 pow() 方法pow(x, y[, z]) 函数是计算x的y次方&#xff0c;如…

计算机里pow函数是什么,pow函数是什么?

pow函数&#xff1a; C/C中的数学函数&#xff1b; pow() 函数用来求 x 的 y 次幂(次方)&#xff0c;x、y及函数值都是double型 pow()用来计算以x 为底的 y 次方值&#xff0c;然后将结果返回。设返回值为 ret&#xff0c;则 ret xy。 可能导致错误的情况&#xff1a; 如果底数…

WebClient 连接池配置

背景 项目中调用第三方接口&#xff0c;第三方接口响应时间不稳定&#xff0c;并且并发越大响应时间越长&#xff0c;如果使用BIO模式可能会导致服务器连接占用过高&#xff0c;所以采用WebClient reactor模式来调用第三方接口。 网络IO模型 连接池相关配置 maxConnections…

WebClient用法小结

进来的项目中要实现能够在windows service中调用指定项目的链接页面。由于访问页面时候使用的是ie浏览器或其他浏览器&#xff0c;所以想起用webclient类。 如果只想从特定的URI请求文件&#xff0c;则使用WebClient&#xff0c;它是最简单的.NET类&#xff0c;它只用一两条命…

WebClient类的使用方法(C#---网络编程)

WebClient类的使用方法&#xff08;C#---网络编程&#xff09; 1、WebClient类 WebClient类提供向URI标识的任何本地、Intranet或Internet资源发送数据以及从这些资源接收数据的公共方法。 默认情况下&#xff0c;WebClient实例不能发送可选的HTTP报头。如果要发送可选报头&a…

WebClient 用法总结

一、WebClient概述 WebClient提供向 URI 标识的资源发送数据和从 URI 标识的资源接收数据的公共方法&#xff0c; WebClient 类提供向 URI 标识的任何本地、Intranet 或 Internet 资源发送数据以及从这些资源接收数据的公共方法。 WebClient 类使用 WebRequest 类提供对资源…

C#-WebClient

using ( var wc new System.Net.WebClient() ) {var imagebytes wc.DownloadData( hag.ImagePath );using ( MemoryStream ms new MemoryStream( imagebytes ) ) {picImage.Image Image.FromStream( ms );} } C#中HttpWebRequest、WebClient、HttpClient的使用 HttpWebRe…

webclient学习1.webclient是什么?

1.webclient是什么&#xff1f; WebClient 软件包是 RT-Thread 自主研发的&#xff0c;基于 HTTP 协议的客户端的实现&#xff0c;它提供设备与 HTTP Server 的通讯的基本功能。 2.软件包功能特点 WebClient 软件包功能特点&#xff1a; 支持 IPV4/IPV6 地址 WebClient 软…

SpringBoot - 网络请求客户端WebClient使用详解

在 Spring 5 之前&#xff0c;如果我们想要调用其他系统提供的 HTTP 服务&#xff0c;通常可以使用 Spring 提供的 RestTemplate 来访问&#xff0c;不过由于 RestTemplate 是 Spring 3 中引入的同步阻塞式 HTTP 客户端&#xff0c;因此存在一定性能瓶颈。根据 Spring 官方文档…

【QString 函数学习篇】

【QString 函数学习篇】 【1】UI设计布局【2】QChar | setAlignment |【3】sprintf | asprintf | setNum | number |【4】toInt | toUpper [十进制->十六进制 | 十进制->二进制]【5】clear | append【6】二进制->十六进制 | 二进制->十进制【7】prepend【8】strimm…

Qt扫盲-QString使用总结

QString使用总结 一、概述二、初始化字符串1、极速版2、原理版 三、操作字符串1、极速版1. 增加2. 删除3. 修改4. 插入5. 转换 2、原理版 四、查询字符串五、字符串格式转换六、Null 字符串和 Empty 字符串的区别七、字符串参数格式化八、更高效的字符串构造九、最大大小和出现…

Qt之QString字符串类的详细介绍及功能函数示例用法

前序&#xff1a;为了加深对Qt各个类的认识和了解&#xff0c;决定以后再闲暇的时间对Qt类进行详细的阅读&#xff08;基于Qt cteater 的帮助文档&#xff09;&#xff0c;这样既可以提高自己的英文阅读能力&#xff0c;也可以对各个类有一个更加深入的认识和了解。 注&#xf…

QString类详解

文章目录 一、简要介绍1.1 隐式共享 二、常用方法2.1 常用构造函数2.2 字符串与数值之间的转换2.3 添加字符串2.4 大小写转换2.5 长度2.6 其他常用函数 一、简要介绍 在Qt中表示字符串的类是QString类&#xff0c;它存储字符串是采用的Unicode码&#xff0c;编码方式是使用UTF…

QString(详解)

目录 一、QString转char* 二、合并字符串 三、分割字符串 split()函数 mid(pos,n)函数 QString::section truncate(int)函数&#xff0c;从指定位置截断字符串str 四、去除字符串两端的空白 QString::trimmed()函数 QString::simplified()函数 五、查找是否包含字符…

Java——JLabel自动换行问题

┏(ω)☞ 本专栏的目录&#xff08;为您提供更好的查询方式&#xff09;&#xff08;点这里说不定有你想要的&#xff09; JLabel类可显示文本&#xff0c;一般直接添加文字会导致如下问题&#xff08;字体显示不全&#xff09;&#xff0c;在不同过增加文本框的长度的情况下可…

JLabel展示文本和图片

JLabel 构造方法 JLabel&#xff08;&#xff09;创建一个不含图像&#xff0c;不含文本的 JLabel 对象 JLabel&#xff08;String text&#xff09;使用指定的文本创建一个 JLabel 对象 JLabel&#xff08;Icon image&#xff09;创建一个具有指定图像的 JLabel 对象 pack…