过滤器(Filter)与拦截器(Interceptor)详解
在Java Web开发中,过滤器和拦截器都是实现横切关注点(Cross-Cutting Concerns)的重要技术,用于在请求处理的不同阶段执行预处理和后处理操作。虽然它们在功能上有相似之处,但在实现机制、作用范围和使用场景上有显著区别。
一、过滤器(Filter)
1. 基本概念
过滤器是Servlet规范的一部分,作用于Web容器层面。它可以对请求和响应进行预处理和后处理,过滤范围包括:
- 静态资源(HTML、CSS、JS)
- Servlet
- JSP
- 其他过滤器
2. 生命周期
3. 核心方法
public interface Filter {
// 初始化方法(容器启动时调用)
default void init(FilterConfig filterConfig) {}
// 过滤方法(每次请求调用)
void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 销毁方法(容器关闭时调用)
default void destroy() {}
}
4. 执行流程
5. 典型应用场景
- 字符编码设置
- 敏感词过滤
- 权限验证
- 请求日志记录
- 跨域处理
6. 使用示例:日志过滤器
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
long start = System.currentTimeMillis();
// 请求前处理
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("Request URL: " + req.getRequestURL());
chain.doFilter(request, response); // 继续执行过滤器链
// 响应后处理
long duration = System.currentTimeMillis() - start;
System.out.println("Request processed in " + duration + "ms");
}
}
7. 配置方式
XML配置(web.xml)
<filter>
<filter-name>logFilter</filter-name>
<filter-class>com.example.LogFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>logFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注解配置(Servlet 3.0+)
@WebFilter(urlPatterns = "/*")
public class LogFilter implements Filter {
// ...
}
Spring Boot配置
@Bean
public FilterRegistrationBean<LogFilter> loggingFilter() {
FilterRegistrationBean<LogFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new LogFilter());
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 设置执行顺序
return registration;
}
二、拦截器(Interceptor)
1. 基本概念
拦截器是Spring MVC框架的一部分,作用于Spring容器层面。它只能在Spring MVC的DispatcherServlet之后执行,主要拦截Controller请求。
2. 生命周期
3. 核心方法
public interface HandlerInterceptor {
// Controller执行前调用(返回true继续执行,false中断)
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {}
// Controller执行后,视图渲染前调用
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {}
// 整个请求完成后调用(视图渲染后)
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {}
}
4. 执行流程
5. 典型应用场景
- 权限验证
- 日志记录
- 性能监控
- 通用参数处理
- 接口防刷
6. 使用示例:权限拦截器
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// 检查登录状态
HttpSession session = request.getSession();
if (session.getAttribute("user") == null) {
response.sendRedirect("/login");
return false; // 中断执行
}
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
// 添加通用模型数据
modelAndView.addObject("version", "1.0.0");
}
}
7. 配置方式
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/login", "/register"); // 排除路径
}
}
三、过滤器和拦截器的区别
| 特性 | 过滤器(Filter) | 拦截器(Interceptor) |
|---|---|---|
| 规范 | Servlet规范 | Spring框架 |
| 作用范围 | 所有Web资源(静态资源、Servlet等) | 只拦截Controller请求 |
| 依赖 | 不依赖Spring框架 | 依赖Spring MVC框架 |
| 执行位置 | DispatcherServlet之前 | DispatcherServlet之后,Controller之前 |
| 容器 | Web容器(Tomcat/Jetty等) | Spring容器 |
| 获取对象 | Servlet API对象 | HandlerMethod、ModelAndView等 |
| 异常处理 | 无法获取Controller异常 | 可以获取Controller异常 |
| 配置方式 | web.xml或@WebFilter | WebMvcConfigurer配置 |
| 执行顺序 | 按注册顺序执行 | 按添加顺序执行 |
四、执行顺序对比
完整请求处理流程:
五、如何选择使用
使用过滤器的场景:
- 需要对所有Web资源进行统一处理(包括静态资源)
- 处理与框架无关的底层操作
- 需要处理文件上传/下载
- 实现跨域支持(CORS)
- 请求/响应内容修改
使用拦截器的场景:
- 需要访问Controller方法的元数据
- 需要修改ModelAndView对象
- 需要处理Controller抛出的异常
- 实现基于方法级别的拦截
- 需要依赖Spring容器中的Bean
六、高级应用技巧
1. 过滤器链中的参数传递
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
// 设置请求属性
request.setAttribute("startTime", System.currentTimeMillis());
chain.doFilter(request, response);
// 获取响应信息
long duration = System.currentTimeMillis() - (long) request.getAttribute("startTime");
}
}
2. 拦截器中的异常处理
public class GlobalInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
if (ex != null) {
// 记录异常日志
log.error("Request processing error", ex);
// 自定义错误响应
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().write("{\"error\":\"Internal Server Error\"}");
}
}
}
3. 组合使用案例:API接口监控
// 过滤器:记录请求开始时间和基础信息
public class MonitoringFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
request.setAttribute("startTime", System.currentTimeMillis());
chain.doFilter(request, response);
}
}
// 拦截器:记录详细执行信息
public class MonitoringInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
request.setAttribute("controller", method.getBean().getClass().getSimpleName());
request.setAttribute("method", method.getMethod().getName());
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
long startTime = (Long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
String controller = (String) request.getAttribute("controller");
String method = (String) request.getAttribute("method");
// 记录监控日志
log.info("Controller: {}, Method: {}, Duration: {}ms",
controller, method, duration);
}
}
七、常见问题解答
Q1:过滤器和拦截器哪个先执行?
A:在请求处理阶段,过滤器的执行顺序优先于拦截器:
请求 -> 过滤器1 -> 过滤器2 -> DispatcherServlet -> 拦截器1 -> 拦截器2 -> Controller
Q2:可以同时使用多个过滤器和拦截器吗?
A:可以。多个过滤器按注册顺序形成过滤器链,多个拦截器按添加顺序形成拦截器链。
Q3:如何在过滤器中访问Spring Bean?
A:通过FilterRegistrationBean注入:
@Bean
public FilterRegistrationBean<MyFilter> myFilter(MyService service) {
FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new MyFilter(service)); // 通过构造函数注入
return bean;
}
Q4:拦截器preHandle返回false会怎样?
A:请求将被中断,后续拦截器和Controller都不会执行,但已通过preHandle的拦截器的afterCompletion会被调用。
Q5:如何控制过滤器的执行顺序?
A:在Spring Boot中使用@Order注解或FilterRegistrationBean设置顺序:
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
public Filter filterA() {
return new FilterA();
}
@Bean
public FilterRegistrationBean<FilterB> filterB() {
FilterRegistrationBean<FilterB> bean = new FilterRegistrationBean<>();
bean.setFilter(new FilterB());
bean.setOrder(2); // 设置执行顺序
return bean;
}
八、最佳实践建议
-
职责分离原则:
- 过滤器负责底层、通用的请求/响应处理
- 拦截器负责业务相关的横切关注点
-
性能优化:
- 在过滤器中执行资源密集型操作(如GZIP压缩)
- 在拦截器中执行轻量级操作(如参数校验)
-
安全性考虑:
- 在过滤器中实现基础安全防护(如XSS过滤)
- 在拦截器中实现业务权限控制
-
异常处理:
- 在过滤器中处理Servlet级别的异常
- 在拦截器中处理Controller级别的异常
-
配置管理:
- 使用Spring配置管理拦截器
- 使用FilterRegistrationBean管理过滤器依赖
-
调试技巧:
// 打印过滤器链 request.getServletContext().getFilterRegistrations().forEach((name, reg) -> { System.out.println("Filter: " + name + ", URLs: " + reg.getUrlPatternMappings()); }); // 打印拦截器链 ((AbstractHandlerMapping) handlerMapping).getAdaptedInterceptors() .forEach(interceptor -> System.out.println("Interceptor: " + interceptor));
通过合理使用过滤器和拦截器,可以实现高度可复用的横切关注点处理逻辑,提高代码的可维护性和系统的可扩展性。
2394

被折叠的 条评论
为什么被折叠?



