那个让我差点翻车的Spring面试题:拦截器和过滤器到底啥区别?🕵️♂️
朋友们,今天我想分享一个让我在面试中差点翻车的经历------说说Spring里的拦截器(Interceptor)和过滤器(Filter)有什么区别。
说实话,当时面试官问我的时候,我内心OS是:这不都差不多吗?结果支支吾吾半天没说明白,场面一度十分尴尬😅
从翻车到真香的心路历程
初识:我以为它们只是名字不同
刚开始学Spring的时候,我真的以为拦截器和过滤器是同一个东西的不同叫法。就像番茄和西红柿,土豆和马铃薯,本质上没啥区别嘛!
但后来在实际项目中踩了几个坑才发现,「它们根本不是一回事」!
醒悟:原来差别这么大!
让我用一个生活化的例子来解释:
想象一下你去银行办业务:
- 「过滤器」就像银行的「大门保安」 - 所有进出的人都要经过他检查(包括来存钱的、来取款的、甚至走错门的)
- 「拦截器」就像银行的「柜台经理」 - 只有真正来办业务的人才会遇到他,而且他只在具体业务流程中介入
是不是突然有点感觉了?别急,咱们深入聊聊。
技术层面的深度剖析
过滤器(Filter):尽忠职守的门卫
过滤器是Servlet规范中的概念,它不依赖任何框架。也就是说,即使你不用Spring,只用最基础的Servlet,也能使用过滤器。
java
// 过滤器示例:记录请求日志
@WebFilter("/*")
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
System.out.println("过滤器:请求开始 - " + ((HttpServletRequest) request).getRequestURL());
// 必须调用doFilter,否则请求就卡在这了
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
System.out.println("过滤器:请求结束,耗时:" + (endTime - startTime) + "ms");
}
}
✅ 「过滤器的特点」:
- 工作在Servlet层面,是所有请求的"第一道防线"
- 可以获取原始的ServletRequest和ServletResponse
- 对所有请求都有效,包括静态资源
拦截器(Interceptor):业务流程的监督员
拦截器是Spring MVC框架的一部分,它更懂Spring的"语言"。
java
// 拦截器示例:权限验证
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (!isValidToken(token)) {
response.sendError(401, "Token无效");
return false; // 中断请求
}
System.out.println("拦截器:权限验证通过");
return true; // 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器:Controller执行完毕");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器:请求完全结束");
}
private boolean isValidToken(String token) {
// 实际的token验证逻辑
return "valid-token".equals(token);
}
}
// 配置拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/**") // 只拦截api路径
.excludePathPatterns("/api/public/**"); // 排除公开接口
}
}
✅ 「拦截器的特点」:
- 可以精确控制拦截哪些路径
- 能获取到Spring的Handler信息
- 可以使用Spring的依赖注入
核心区别:一张图看懂
特性 | 过滤器(Filter) ❌ | 拦截器(Interceptor) ✅ |
---|---|---|
依赖框架 | Servlet规范 | Spring MVC |
获取Spring Bean | 不行 | 可以 |
执行时机 | 最早 | 在DispatcherServlet之后 |
路径匹配 | 相对简单 | 更灵活精确 |
访问Handler信息 | 不能 | 可以 |
那个让我豁然开朗的"啊哈"时刻
最让我震惊的发现是:「它们的执行顺序完全不同!」
一个请求的生命周期是这样的:
- ✅ 先经过过滤器
- ✅ 到达Spring的DispatcherServlet
- ✅ 再经过拦截器
- ✅ 最后才到我们的Controller
也就是说,过滤器是"外援",拦截器是"内部员工"。这下你明白为什么拦截器能享受Spring的各种福利(比如依赖注入),而过滤器不行了吧?
实际开发中怎么选?
经过这么多项目实践,我总结了一个简单原则:
- 需要**「通用处理」(比如日志、编码转换):用「过滤器」**
- 需要**「业务相关」处理(比如权限、参数校验):用「拦截器」**
举个栗子🌰:
- 记录所有请求的访问日志 → 过滤器
- 检查用户是否有权限访问某个API → 拦截器
写在最后
现在回想起来,那次面试翻车其实是件好事。正因为那次尴尬,我才真正搞懂了这个知识点,后来在团队里还成了这方面的"专家"。
所以说,朋友们,遇到不懂的问题不可怕,可怕的是不懂装懂。坦诚面对自己的知识盲区,然后狠狠地把它们补上,这才是我们程序员的成长之道啊!
「互动时间」:你在面试中遇到过哪些让你印象深刻的问题?欢迎在评论区分享你的故事~说不定下次我就写你提到的那个话题!😉
PS:如果这篇文章帮到了你,记得点赞收藏哦~下次见!