SpringBoot 拦截器和过滤器的区别

发布于:2025-07-12 ⋅ 阅读:(20) ⋅ 点赞:(0)

SpringBoot 拦截器和过滤器的区别

SpringBoot 中的拦截器(Interceptor)和过滤器(Filter)都是用于请求处理的增强机制,但它们存在以下核心区别:

对比维度 过滤器(Filter) 拦截器(Interceptor)
所属规范 Servlet 规范( javax.servlet 包) Spring 框架( org.springframework 包)
应用范围 所有进入容器的请求(包括静态资源) 仅针对 Spring MVC 控制器的请求
触发时机 在 Servlet 容器处理请求前/后 在 Spring MVC 处理器执行前/后/完成时
实现方式 实现 javax.servlet.Filter 接口 实现 org.springframework.web.servlet.HandlerInterceptor 接口
依赖注入 不支持(Filter 由 Servlet 容器管理) 支持(Interceptor 由 Spring 容器管理)
访问上下文 仅能访问 ServletRequest 和 ServletResponse 可访问 Handler、ModelAndView 等 Spring 组件
执行顺序 基于 FilterRegistrationBean 配置的顺序 基于 InterceptorRegistry 注册的顺序

设计模式分析

过滤器(Filter)采用的设计模式
  1. 责任链模式(Chain of Responsibility)

    • 多个 Filter 形成链式调用,每个 Filter 可以决定是否继续传递请求到下一个环节
    • 示例代码体现:
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
          throws IOException, ServletException {
          // 前置处理
          chain.doFilter(request, response); // 传递到下一个过滤器或Servlet
          // 后置处理
      }
      
  2. 装饰器模式(Decorator)

    • 通过 Filter 包装原始请求/响应对象,增强其功能(如字符编码处理)
    • 示例:CharacterEncodingFilter 装饰请求对象以设置字符编码
拦截器(Interceptor)采用的设计模式
  1. AOP(面向切面编程)

    • 拦截器本质是 Spring AOP 的一种实现,通过对 Handler 方法的环绕通知实现增强
    • 示例:
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
          throws Exception {
          // 前置处理,返回true继续执行后续拦截器和Handler
          return true;
      }
      
  2. 观察者模式(Observer)

    • 拦截器的 postHandleafterCompletion 方法类似于事件监听机制
    • 当请求处理完成或发生异常时,触发相应的回调方法

典型应用场景对比

场景 过滤器(Filter) 拦截器(Interceptor)
请求编码处理 CharacterEncodingFilter -
跨域请求处理 CorsFilter -
权限校验 基于 Servlet API 的简单权限检查 基于 Spring Security 的复杂权限控制
请求日志记录 记录原始请求信息 记录完整的处理时间和结果
事务管理 - 基于 @Transactional 注解的事务拦截
性能监控 统计请求总耗时 统计 Controller 方法执行耗时

选择建议

  • 使用过滤器:需要对所有请求进行统一处理(如编码、安全头设置)
  • 使用拦截器:需要访问 Spring 容器中的 Bean 或处理 MVC 相关对象(如 ModelAndView)
  • 组合使用:复杂场景下,可结合过滤器和拦截器实现多层级处理(如:过滤器处理基础安全,拦截器处理业务权限)

下面将详细介绍Spring Boot中过滤器(Filter)和拦截器(Interceptor)的使用方法,并提供示例代码:

一、过滤器(Filter)的使用步骤

1. 创建Filter类并实现Filter接口
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*", filterName = "requestLogFilter")
public class RequestLogFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 前置处理:记录请求信息
        System.out.println("Filter: 请求URL - " + httpRequest.getRequestURI());
        
        // 传递请求到下一个过滤器或Servlet
        chain.doFilter(request, response);
        
        // 后置处理:记录响应信息
        System.out.println("Filter: 请求处理完成");
    }
}
2. 启用Servlet组件扫描(如果未使用@ServletComponentScan)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan  // 启用Servlet组件扫描
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
3. 或通过Java配置注册Filter(推荐方式)
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<RequestLogFilter> loggingFilter() {
        FilterRegistrationBean<RequestLogFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new RequestLogFilter());
        registrationBean.addUrlPatterns("/*");  // 拦截所有请求
        registrationBean.setOrder(1);  // 设置过滤器执行顺序
        return registrationBean;
    }
}

二、拦截器(Interceptor)的使用步骤

1. 创建Interceptor类并实现HandlerInterceptor接口
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RequestTimeInterceptor implements HandlerInterceptor {

    private ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 前置处理:记录请求开始时间
        long startTime = System.currentTimeMillis();
        startTimeThreadLocal.set(startTime);
        System.out.println("Interceptor: 请求开始 - " + request.getRequestURI());
        return true;  // 返回true继续执行后续拦截器和Handler
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) {
        // 后置处理:Controller方法执行完成后,但视图渲染前
        System.out.println("Interceptor: 请求处理中 - " + request.getRequestURI());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                Exception ex) {
        // 完成处理:视图渲染完成后,可用于资源清理
        long startTime = startTimeThreadLocal.get();
        long endTime = System.currentTimeMillis();
        System.out.println("Interceptor: 请求完成 - " + request.getRequestURI() 
                          + " 耗时: " + (endTime - startTime) + "ms");
        startTimeThreadLocal.remove();  // 防止内存泄漏
    }
}
2. 注册拦截器到Spring MVC
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 RequestTimeInterceptor())
                .addPathPatterns("/**")  // 拦截所有请求
                .excludePathPatterns("/static/**", "/error");  // 排除静态资源和错误页面
    }
}

三、过滤器 vs 拦截器执行顺序示例

执行顺序配置
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册第一个拦截器(order=1)
        registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**").order(1);
        // 注册第二个拦截器(order=2)
        registry.addInterceptor(new SecondInterceptor()).addPathPatterns("/**").order(2);
    }
}

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<FirstFilter> firstFilter() {
        FilterRegistrationBean<FirstFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new FirstFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(1);  // 过滤器顺序1
        return registration;
    }

    @Bean
    public FilterRegistrationBean<SecondFilter> secondFilter() {
        FilterRegistrationBean<SecondFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new SecondFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(2);  // 过滤器顺序2
        return registration;
    }
}
执行顺序结果

对于一个HTTP请求,执行顺序为:

FirstFilter.pre -> SecondFilter.pre -> 
FirstInterceptor.pre -> SecondInterceptor.pre -> 
Controller处理请求 -> 
SecondInterceptor.post -> FirstInterceptor.post -> 
视图渲染 -> 
SecondInterceptor.after -> FirstInterceptor.after -> 
SecondFilter.post -> FirstFilter.post

四、常见应用场景代码示例

1. 权限校验拦截器
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String token = request.getHeader("Authorization");
        
        if (token == null || !validateToken(token)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授权");
            return false;
        }
        
        // 将用户信息存入Request,供后续使用
        request.setAttribute("user", parseUserFromToken(token));
        return true;
    }
    
    private boolean validateToken(String token) {
        // 验证Token逻辑
        return token.startsWith("Bearer ");
    }
    
    private User parseUserFromToken(String token) {
        // 解析用户信息
        return new User("testUser");
    }
}
2. 字符编码过滤器
@WebFilter(urlPatterns = "/*", filterName = "encodingFilter")
public class EncodingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        chain.doFilter(request, response);
    }
}

五、关键配置说明

配置项 过滤器(Filter) 拦截器(Interceptor)
拦截路径配置 @WebFilter(urlPatterns = "/*") registry.addPathPatterns("/**")
排除路径配置 不直接支持,需在doFilter中手动判断 registry.excludePathPatterns("/static/**")
执行顺序控制 通过 FilterRegistrationBean.setOrder() 通过 addInterceptor().order() 或注册顺序
依赖注入 不支持(非Spring管理) 支持(可注入Service等Bean)
获取Spring上下文 需手动获取ApplicationContext 可直接注入所需组件

六、注意事项

  1. 过滤器注意点

    • Filter由Servlet容器管理,生命周期独立于Spring
    • 无法直接注入Spring Bean,需通过ApplicationContext获取
    • 对所有请求生效,包括静态资源和错误请求
  2. 拦截器注意点

    • Interceptor由Spring管理,可使用依赖注入
    • 仅拦截Spring MVC处理的请求,对静态资源无效
    • 可访问HandlerMethod、ModelAndView等Spring MVC特有对象
  3. 性能考虑

    • 过滤器执行更早,性能开销更小
    • 拦截器功能更强大,但依赖Spring MVC上下文

根据实际需求合理选择过滤器或拦截器,复杂场景可组合使用两者。


网站公告

今日签到

点亮在社区的每一天
去签到