拦截器和过滤器(理论+实操)
拦截器和过滤器
本文旨在夯实基础以及实战加深理解,目的是更深的理解以便掌握,希望能跟着动手敲一遍,绝对受益匪浅
在本文,我会先给出两者的区别(理论知识),随后是两者各自的实操实现
文章目录
- 拦截器和过滤器
- 什么是过滤器和拦截器?
- 1.过滤器
- 2.拦截器
- 执行整体流程
- 拦截器和过滤器的区别
- 实操:
- 1.过滤器
- 1.定义日志记录过滤器类`LogFilter`
- 2.在主启动类上**添加注解**`@ServletComponentScan`
- 2.拦截器
- 1.创建拦截器
- 2.注册拦截器
- 测试
- 好了,你学会没?
什么是过滤器和拦截器?
1.过滤器
首先说下什么是过滤器?
- 是
Java Servlet
规范中定义的标准,是Servlet
一部分,- 配置也是非常简单,直接实现
javax.servlet.Filter
接口就可以,- 也可以用注解
@WebFilter
对特定的URL
拦截
2.拦截器
那拦截器又是什么东东?
- 是
Spring
框架自身提供的组件,也就是说必须依赖SpringMvc
框架才能使用,是位于Spring
的上下文之中- 是
AOP
的一种实现,支持链式调用,通过类HandlerInterceptor
实现多个拦截- 说白了:就是切面编程典型实现,允许程序猿在一些核心业务操作执行的前后插入自定义的逻辑代码,如日志记录,权限认证等,而无需修改核心代码的本身
执行整体流程
我们首先看下过滤器和拦截器在整个流程中的执行顺序
解释说明:(结合上图)
- Filter 开始:
HTTP
请求进入Tomcat
容器。- Filter 链处理:请求会经过所有配置的
Filter
的doFilter()
方法。- 进入
Spring MVC
:请求到达DispatcherServlet
(Spring MVC 的核心控制器)。- Interceptor 开始:
DispatcherServlet
分发请求前,会先执行拦截器链的preHandle
方法。- Controller 执行:如果
preHandle
返回true
,请求被分发到对应的Controller
方法执行。- Interceptor 后处理:
Controller
执行完毕后,返回ModelAndView
,然后执行拦截器链的postHandle
方法。- 视图渲染完毕后(或请求处理完成后),执行拦截器链的
afterCompletion
方法。- Filter 结束:响应最终再次经过 Filter 链的
doFilter()
方法(反向),返回给客户端。
拦截器和过滤器的区别
特性 | 过滤器 (Filter) | 拦截器 (Interceptor) |
---|---|---|
归属与依赖 | Servlet 规范,不依赖 Spring | Spring 框架 的组件 |
作用范围 | 作用于所有进入容器的请求(包括静态资源,如 /css/ , /js/ ) | 只作用于 Spring MVC 处理的请求(即 Controller 的请求,通常不会拦截静态资源) |
实现原理 | 基于 函数回调 (doFilter() ) | 基于 Java 反射和动态代理 |
使用方式 | 在 web.xml 中配置或使用 @WebFilter 注解 | 在 Spring MVC 配置中注册 (WebMvcConfigurer ) |
获取 IOC 容器 | 无法 直接获取 Spring 的 IOC 容器和其中的 Bean | 可以,因为它本身就是 Spring 管理的,可以轻松使用 @Autowired 注入其他 Bean |
执行时机/位置 | 最早。 1. 在请求进入 Tomcat 等容器之后 2. 在进入 Servlet 之前 3. 在 Servlet 处理完之后,返回给客户端之前 | 较晚。 1. 在请求进入 DispatcherServlet 之后 2. 在进入 Controller 之前 (preHandle) 3. 在 Controller 执行之后,视图渲染之前 (postHandle) 4. 在整个请求结束之后 (afterCompletion) |
功能侧重 | 更底层,关注的是请求本身(Request/Response),功能强大。 例如:字符编码设置、权限校验(粗粒度)、日志记录、性能监控、解压/gzip压缩。 | 更上层,关注的是 Controller 的方法执行,与业务结合更紧密。 例如:更精细的权限校验(判断Token、角色)、日志记录(记录Controller方法、参数)、参数预处理、Controller 执行时间计算。 |
实操:
1.过滤器
案例: 在Springboot
中实现日志记录过滤器
1.定义日志记录过滤器类LogFilter
主要的三大核心方法:(可以结合上面的整体执行流程图来看)
init()
:该方法在容器启动初始化过滤器时被调用,在Filter
的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器不生效,没有作用。doFilter()
:容器中的每一次请求都会调用该方法,FilterChain
用来调用下一个过滤器Filter
。destroy()
: 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器Filter
的整个生命周期也只会被调用一次
package com.zhengqian.test01aop.filter;import lombok.extern.slf4j.Slf4j;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;/*** 日志过滤器: * @author zhengqian* @since 2025年09月04日 14:56*/
@WebFilter(urlPatterns = "/*")//过滤所有的请求
@Slf4j
public class LogFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);//初始化执行log.info("***^^^日志过滤器初始化完成");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//记录请求的开始时间long startTime = System.currentTimeMillis();//转换为HttpServletRequest获取更多信息HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;//获取基本信息String requestURI = httpServletRequest.getRequestURI();String method = httpServletRequest.getMethod();String remoteAddr = httpServletRequest.getRemoteAddr();//log.info("请求开始=> 方法:{} 路径: {}} IP:{}",method,requestURI,remoteAddr);try {filterChain.doFilter(servletRequest,servletResponse);} finally {//计算处理耗时long duration = System.currentTimeMillis() - startTime;//响应日志log.info("请求结束 ==> 路径: {} 耗时:{}",requestURI,duration);}}@Overridepublic void destroy() {Filter.super.destroy();log.info("日志过滤器销毁");}
}
2.在主启动类上添加注解@ServletComponentScan
OK!这个时候你启动就可以看到此过滤器成功!
你可能会问?这么简单?对的,就是这么简单!前提你要跟着做!哈哈哈!
2.拦截器
1.创建拦截器
核心方法解释:
preHandle()
:
- 此方法在请求处理之前进行调用。
- 注意:如果该方法的返回值为
false
,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。postHandle()
:
- 只有在
preHandle()
方法返回值为true
时才会执行。- 会在
Controller
中的方法调用之后,DispatcherServlet
返回渲染视图之前被调用。afterCompletion()
:只有在preHandle()
方法返回值为true
时才会执行。在整个请求结束之后,DispatcherServlet
渲染了对应的视图之后执行。
package com.zhengqian.test01aop.interceptor;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 自定义拦截器的实现* @author zhengqian* @since 2025年09月04日 15:13*/
@Slf4j
public class CustomInterceptor implements HandlerInterceptor {/*** 请求处理前被调用* @param request 请求* @param response 响应* @param handler 处理器* @return aa* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//添加校验权限log.info("开始出发拦截器校验前的处理------------->");return true;}/*** **其中**:`postHandle()` 方法被调用的顺序跟 `preHandle()` 是**相反**的,先声明的拦截器 `preHandle()` 方法先执行,而`postHandle()`方法反而会后执行。* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("只有在 preHandle() 方法返回值为true 时才会执行");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("整个请求结束之后被调用");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}}
2.注册拦截器
addPathPatterns
:需要拦截的路径
excludePathPatterns
: 不需要拦截的路径
package com.zhengqian.test01aop.config;import com.zhengqian.test01aop.interceptor.CustomInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author zhengqian* @since 2025年09月04日 15:19*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册拦截器并设置拦截路径registry.addInterceptor(new CustomInterceptor())//拦截所有.addPathPatterns("/**")//排除*/login的登录路径.excludePathPatterns("/aop/login");}
}
测试
我们建一个测试类,来进行上面的拦截测试
package com.zhengqian.test01aop.controller;import com.zhengqian.test01aop.service.TestService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @author zhengqian* @date 2025年07月21日 16:40*/
@RestController
@RequestMapping("/aop")
public class TestController {@ResourceTestService testService;@GetMapping("/test")public String test(){testService.test();return "测试成功!----";}@GetMapping("/login")public String login(){testService.test();return "测试成功!----";}
}
打印日志:
1.走拦截器