过滤器与拦截器 ⚠️
对应 PHP 中的 HTTP 中间件(Middleware),但 Spring Boot 把"请求前后处理"拆成了两种机制。
两种机制对比
| 维度 | Filter | HandlerInterceptor |
|---|---|---|
| 所属层级 | Servlet 容器级(Tomcat) | Spring MVC 级 |
| 是否能处理静态资源 | 是 | 否(只拦截 Controller 请求) |
| 是否能修改请求/响应对象 | 是 | 是 |
| 是否知道 Controller 信息 | 否(只看到 URL) | 是(可以知道调用了哪个方法) |
| 和 Laravel Middleware 的对应 | 接近 $next($request) | 更精确的控制器前后处理 |
HandlerInterceptor(推荐,功能更强)
定义
java
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 类比 PHP 中间件的 handle($request, $next) 之前
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
response.setStatus(401);
response.getWriter().write("{\"error\":\"unauthorized\"}");
return false; // 终止请求,不继续
}
return true; // 继续执行控制器
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// 类比 PHP 中间件的 handle($request, $next) 之后,视图渲染之前
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// 请求完全结束后(视图渲染完成),类比 PHP 的 terminate 中间件
}
}注册
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Autowired
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor)
.addPathPatterns("/**"); // 拦截所有路径
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**") // 只拦截 /api/*
.excludePathPatterns("/api/login"); // 排除登录接口
}
}Filter(更低级,通常用不到)
java
@Component
@Order(1) // 排序,数字越小越先执行
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Access-Control-Allow-Origin", "*");
chain.doFilter(request, response); // 类比 return $next($request)
}
}⚠️ 和 PHP 中间件的关键差异
1. 不能像 Laravel 那样直接 return response
php
// PHP 中间件
public function handle($request, Closure $next) {
if (!$request->has('token')) {
return response()->json(['error' => 'unauthorized'], 401);
}
return $next($request);
}java
// Java 拦截器:不能 return 响应,必须手动写入 response 对象
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (request.getHeader("Authorization") == null) {
response.setStatus(401);
response.getWriter().write("{\"error\":\"unauthorized\"}");
return false; // 返回 false 表示"拦截,不要再继续"
}
return true;
}原因:Servlet 规范在设计上,Filter/Interceptor 是管道模式,没有返回值作为响应体的机制。你需要直接操作
HttpServletResponse。 这是 Java Servlet 的历史设计决定的,Spring 也无法改变。
2. 多个拦截器的顺序需要手动注册
php
// PHP:在 Kernel 中定义数组顺序
protected $middlewarePriority = [
\App\Http\Middleware\Authenticate::class,
\App\Http\Middleware\LogRequests::class,
];java
// Java:通过 @Order 或 Ordered 接口控制
@Order(1) public class LogInterceptor implements HandlerInterceptor { ... }
@Order(2) public class AuthInterceptor implements HandlerInterceptor { ... }3. 没有 after() 方法的简易语法
php
// PHP 中可以在中间件中写前后逻辑
$response = $next($request);
// 这里是 after 逻辑
return $response;java
// Java 中 before 和 after 是分开的方法
// before → preHandle(return true 才继续)
// after → postHandle(执行完控制器后)
// finally → afterCompletion(无论如何都会执行)4. ⭐ 修改请求体很麻烦
在 Filter 中读了一次请求 Body 后,后面的 Controller 就读取不到了,因为 ServletInputStream 只能读一次。 需要
ContentCachingRequestWrapper包装,这是 Java Servlet 规范的限制。