Spring的拦截器和过滤器的区别
拦截器和过滤器的区别
1. 定义与归属
维度 | Filter(过滤器) | Interceptor(拦截器) |
---|---|---|
属于 | Servlet 规范(JavaEE) | SpringMVC 框架机制 |
管理 | 由 Servlet 容器(如 Tomcat)管理 | 由 Spring 容器管理 |
本质 | 是一种基于函数回调的请求预处理机制 | 是基于 Java 反射机制的请求拦截器 |
依赖注入 | 不支持直接注入 Spring Bean(需特殊配置) | 支持直接注入 Spring Bean |
2. 拦截范围
维度 | Filter | Interceptor |
---|---|---|
作用范围 | 所有请求(包括静态资源,如 HTML、JS、CSS、图片) | 只拦截经过 DispatcherServlet 的请求,即控制器 Controller 相关请求 |
典型请求 | 包括 API、静态资源、文件上传下载等 | 仅 Controller 层及之后流程 |
3. 生命周期和调用时机
Filter 生命周期:
-
服务器启动时初始化。
-
请求到达 Servlet 之前执行。
-
请求完成后也可进行后处理(通过 FilterChain.doFilter 前后分别处理)
工作流程:
请求 → Filter → DispatcherServlet → Interceptor → Controller
Interceptor 生命周期:
-
DispatcherServlet 初始化时注册拦截器。
-
在 Controller 方法调用前/后执行,更靠近业务逻辑层。
具有 3 个关键回调方法:
-
preHandle():请求处理前调用(可以中断请求)
-
postHandle():请求处理后,视图渲染前调用
-
afterCompletion():整个请求完成后调用(用于资源清理)
工作流程:
请求 → Filter → DispatcherServlet → preHandle() → Controller → postHandle() → 渲染 → afterCompletion()
4. 作用和功能定位
维度 | Filter 的主要作用 | Interceptor 的主要作用 |
---|---|---|
常用于 | 编码设置、跨域处理(CORS)、日志记录、安全校验、统一异常处理 | 登录鉴权、权限控制、业务日志记录、参数加工 |
粒度 | 粗粒度(请求级别处理) | 细粒度(控制器方法级别处理) |
静态资源处理 | 可以直接处理(比如压缩、缓存控制) | 默认不处理(静态资源跳过 DispatcherServlet) |
总结
flowchart TD
A(请求进入服务器) --> B(Filter链处理)
B --> C(DispatcherServlet)
C --> D(Interceptor.preHandle)
D --> E(Controller方法执行)
E --> F(Interceptor.postHandle)
F --> G(View解析)
G --> H(Interceptor.afterCompletion)
H --> I(响应返回客户端)
Demo
1. 自定义 Filter - LogFilter.java
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@Order(1) // 可以指定顺序,数字越小优先执行
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("[LogFilter] 请求路径: " + req.getRequestURI());
chain.doFilter(request, response);
}
}
2. 自定义 Interceptor - LoginInterceptor.java
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("Authorization");
if (!"valid-token".equals(token)) {
System.out.println("[LoginInterceptor] 未登录,拦截请求!");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
System.out.println("[LoginInterceptor] 登录验证通过");
return true;
}
}
3. 配置注册 - WebConfig
import org.example.springmvclearn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/**") // 只拦截 /api 开头的路径
.excludePathPatterns("/api/public/**"); // 排除不需要登录的接口
}
}
4. 测试 Controller 示例
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class TestController {
@GetMapping("/public/hello")
public String publicHello() {
return "这是公开接口,不需要登录";
}
@GetMapping("/private/hello")
public String privateHello() {
return "这是私有接口,需要登录验证";
}
}
5. 测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class SpringMvcLearnApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void testPublicHello() throws Exception {
mockMvc.perform(get("/api/public/hello"))
.andExpect(status().isOk())
.andExpect(content().string("这是公开接口,不需要登录"));
}
@Test
public void testPrivateHelloWithoutToken() throws Exception {
mockMvc.perform(get("/api/private/hello"))
.andExpect(status().isUnauthorized());
}
@Test
public void testPrivateHelloWithValidToken() throws Exception {
mockMvc.perform(get("/api/private/hello")
.header("Authorization", "valid-token"))
.andExpect(status().isOk())
.andExpect(content().string("这是私有接口,需要登录验证"));
}
}
6.结果
[LogFilter] 请求路径: /api/public/hello
[LogFilter] 请求路径: /api/private/hello
[LoginInterceptor] 未登录,拦截请求!
[LogFilter] 请求路径: /api/private/hello
[LoginInterceptor] 登录验证通过