在 java web 开发中,过滤器 (filter) 和 拦截器 (interceptor) 都是常用的 aop(面向切面编程)实现方式,但它们所属的框架层级和执行时机有很大不同。
我们可以把它们想象成进入大楼的保安(过滤器)和进入办公室前的秘书(拦截器)。
1. 过滤器 (filter)
过滤器属于 servlet 规范,它位于所有 servlet 之前。它可以对几乎所有的请求(html、图片、js、路径等)进行预处理。
实现方案一:通过@webfilter注解(servlet 原生)
这是最简单、最直接的方式,不需要额外的配置类。
- 实现步骤:
- 实现
jakarta.servlet.filter接口。 - 重写
dofilter方法。 - 在类上标注
@webfilter(urlpatterns = "/*")。 - 在启动类上加
@servletcomponentscan。
- 实现
实现方案二:通过filterregistrationbean(spring boot统一管理生命周期 推荐)
如果你需要更精细地控制过滤器的执行顺序(order),或者你的过滤器是第三方库提供的,这种方案最合适。
- 实现步骤:
- 编写一个普通的类实现
filter接口。 - 创建一个配置类(标注
@configuration)。 - 定义一个
bean返回filterregistrationbean实例。
- 编写一个普通的类实现
@bean
public filterregistrationbean<myfilter> registrationbean() {
filterregistrationbean<myfilter> bean = new filterregistrationbean<>();
bean.setfilter(new myfilter()); // 设置过滤器
bean.addurlpatterns("/api/*"); // 设置拦截路径
bean.setorder(1); // 设置优先级,数字越小越靠前
return bean;
}2. 拦截器 (interceptor)
拦截器属于 spring mvc 框架。它只能拦截发送到 controller 的请求。因为它在 spring 容器中,所以它可以直接使用 spring 注入的对象(比如 service)。
需要继承/实现的类
- 实现接口:
org.springframework.web.servlet.handlerinterceptor。 - 主要方法:
prehandle:在 controller 执行前调用(常用作权限校验)。posthandle:controller 执行后,视图渲染前调用。aftercompletion:整个请求结束(视图渲染后)调用,常用于清理资源。
注册拦截器
实现拦截器后,必须将其注册到 spring mvc 的配置中:
- 继承
webmvcconfigurer接口。 - 重写
addinterceptors方法。
3. 核心区别对比表
| 特性 | 过滤器 (filter) | 拦截器 (interceptor) |
| 所属框架 | servlet 容器 (如 tomcat) | spring mvc 框架 |
| 实现接口 | jakarta.servlet.filter | handlerinterceptor |
| 拦截范围 | 几乎所有请求 (url 匹配即可) | 仅针对 controller 的请求 |
| 注入能力 | 较难直接注入 spring bean | 完美支持注入 bean |
| 执行时机 | 在 servlet 之前执行 | 在 servlet 之后,controller 之前执行 |
| 主要用途 | 编码设置、敏感词过滤、全局压缩 | 权限检查、日志记录、参数预处理 |
简单总结:
- 过滤器:适合做底层的、通用的操作(如:字符编码、请求跨域、全局加密)。
- 拦截器:适合做业务相关的操作(如:检查是否登录、接口权限控制、记录操作日志)。
为什么跨域请求要通过过滤器实现?
其实跨域(cors)既可以通过**过滤器(filter)实现,也可以通过拦截器(interceptor)**或者 spring 提供的 @crossorigin 注解实现。
但在实际生产中,绝大多数架构师会首选过滤器。这主要源于跨域请求的一个特殊机制:预检请求(preflight request)。
1. 预检请求(options)的拦路虎
当浏览器发现你在发起一个“复杂请求”(比如带了自定义 header 或 content-type 为 application/json)时,它会先自动发送一个 options 方法的探测请求。
- 过滤器的优势:由于过滤器在 servlet 容器的最外层,它能在请求到达 spring mvc 的
dispatcherservlet之前,就识别出这是一个 options 请求,并直接返回允许跨域的响应头(200 ok)。 - 拦截器的劣势:拦截器工作在 spring mvc 内部。如果你的 options 请求因为某些安全配置或权限校验没能通过
dispatcherservlet的映射,拦截器可能根本不会执行。这会导致预检请求失败,浏览器直接拦截掉后续的正式请求。
2. 为什么选择过滤器的三大理由
① 执行时机最早(最核心原因)
跨域配置本质上是给 http 响应头加上 access-control-allow-origin 等字段。
- 过滤器在整个过滤链的最前端。如果请求跨域失败,直接在过滤器层级就“挡回去”或“处理掉”了,不会浪费后续的业务逻辑资源(如数据库连接、权限检查)。
② 解决权限框架(security/shiro)的冲突
如果你的项目中使用了 spring security,它本身就是通过一系列过滤器实现的。
- 如果你把跨域放在拦截器里,由于拦截器的执行晚于 spring security 的过滤器,spring security 会在拦截器生效前发现请求没带 token 或不符合安全规则,直接返回 403。
- 此时浏览器因为没收到跨域响应头,会报一个“cors error”,而隐藏了真正的“403 forbidden”错误,导致前端无法准确定位问题。
③ 覆盖范围广
过滤器是针对 url 模式拦截的。如果你的项目里既有 spring mvc 的 controller,又有一些原生的 servlet,或者静态资源,过滤器可以一把抓,统一处理所有资源的跨域问题。
3. 实现代码对比
过滤器方案(推荐)
这种方式利用 spring 提供的 corsfilter,通常配合 filterregistrationbean 使用,优先级设为最高。
@bean
public filterregistrationbean<corsfilter> corsfilter() {
urlbasedcorsconfigurationsource source = new urlbasedcorsconfigurationsource();
corsconfiguration config = new corsconfiguration();
config.setallowcredentials(true);
config.addallowedoriginpattern("*"); // 允许所有来源
config.addallowedheader("*"); // 允许所有header
config.addallowedmethod("*"); // 允许所有方法
source.registercorsconfiguration("/**", config);
filterregistrationbean<corsfilter> bean = new filterregistrationbean<>(new corsfilter(source));
bean.setorder(ordered.highest_precedence); // 确保在最前端执行
return bean;
}总结
跨域选择过滤器,是因为它能在业务逻辑和安全框架之前处理 options 请求。就像在大楼的**正门口(过滤器)贴上“欢迎光临”的告示,比等客人进了办公室(拦截器)**才说欢迎要高效且稳妥得多。
到此这篇关于springmvc(interceptor,filter)实现方案的文章就介绍到这了,更多相关springmvc interceptor,filter内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论