Java过滤器与拦截器深度解析

目录

  1. 概述
  2. 过滤器 Filter
  3. 拦截器 Interceptor
  4. 执行流程图解
  5. 核心区别对比
  6. 代码实现示例
  7. 使用场景
  8. 最佳实践

概述

在Java Web开发中,过滤器(Filter)拦截器(Interceptor) 是两种重要的请求处理机制。它们都能够对HTTP请求进行预处理和后处理,但在实现方式、执行时机和应用场景上有着显著的区别。

关键特征对比表

特征 过滤器 (Filter) 拦截器 (Interceptor)
️ 基于 Servlet规范 Spring框架
⚡ 执行时机 Servlet容器级别 Spring MVC级别
作用域 所有Web请求 Spring管理的请求
配置方式 web.xml 或 @WebFilter Spring配置
依赖注入 不支持 完全支持
异常处理 有限 丰富

过滤器 Filter

基本概念

过滤器是基于Servlet规范的组件,运行在Servlet容器中,能够拦截所有进入应用的HTTP请求和响应。

核心特点

  • 容器级别: 在Servlet容器中运行
  • 全局拦截: 可拦截所有请求,包括静态资源
  • 链式执行: 支持多个过滤器链式调用
  • 标准规范: 基于Java EE标准

Filter接口结构

public interface Filter {
    // 初始化方法,容器启动时调用
    default void init(FilterConfig filterConfig) throws ServletException {}
    
    // 核心过滤方法,每次请求时调用
    void doFilter(ServletRequest request, ServletResponse response, 
                  FilterChain chain) throws IOException, ServletException;
    
    // 销毁方法,容器关闭时调用
    default void destroy() {}
}

生命周期

  1. 初始化阶段: 容器启动时调用 init() 方法
  2. 处理阶段: 每次请求时调用 doFilter() 方法
  3. 销毁阶段: 容器关闭时调用 destroy() 方法

拦截器 Interceptor

基本概念

拦截器是Spring框架提供的机制,专门用于拦截进入Spring MVC的请求,提供更细粒度的控制。

核心特点

  • 框架级别: 在Spring MVC中运行
  • 精确拦截: 只拦截Controller请求
  • 多点拦截: 提供前置、后置、完成后三个拦截点
  • Spring集成: 与Spring容器深度集成

HandlerInterceptor接口结构

public interface HandlerInterceptor {
    // 前置处理:Controller执行前调用
    default boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        return true;
    }
    
    // 后置处理:Controller执行后,视图渲染前调用
    default void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView modelAndView) throws Exception {}
    
    // 完成处理:视图渲染完成后调用
    default void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {}
}

执行时机详解

  1. preHandle: Controller方法执行前,返回false可阻止继续执行
  2. postHandle: Controller方法执行后,视图渲染前,可修改ModelAndView
  3. afterCompletion: 视图渲染完成后,用于清理资源和异常处理

执行流程图解

1. 时序图

下图展示了请求处理过程中各组件的交互时序:

客户端 过滤器 DispatcherServlet 拦截器 Controller 视图 HTTP请求 前置处理 (编码、CORS、权限) chain.doFilter() preHandle() 执行Controller方法 返回ModelAndView postHandle() 渲染视图 视图渲染完成 afterCompletion() 直接返回 alt [preHandle返回true] [preHandle返回false] 返回到过滤器 后置处理 HTTP响应 Filter在Servlet容器层运行 Interceptor在Spring MVC层运行 客户端 过滤器 DispatcherServlet 拦截器 Controller 视图

2. 生命周期对比图

下图展示了过滤器和拦截器在时间轴上的生命周期对比:

0 1 2 3 4 5 6 7 8 9 容器启动-init() 处理请求-doFilter() 请求到达 preHandle() Controller执行 postHandle() 视图渲染 afterCompletion() 容器关闭-destroy() 过滤器生命周期 拦截器生命周期 过滤器 vs 拦截器生命周期对比

核心区别对比 ⚖️

1. 架构层面

  • Filter: 属于Servlet容器层,处理所有HTTP请求
  • Interceptor: 属于Spring MVC层,只处理Controller请求

2. 执行时机

  • Filter: 在Servlet执行前后
  • Interceptor: 在HandlerMapping确定Handler后

3. 配置方式

  • Filter: 通过web.xml或@WebFilter注解配置
  • Interceptor: 通过Spring配置类或XML配置

4. 依赖注入

  • Filter: 不能直接使用Spring的依赖注入(需要特殊处理)
  • Interceptor: 完全支持Spring的依赖注入

5. 异常处理

  • Filter: 无法捕获Controller抛出的异常
  • Interceptor: 可以在afterCompletion中处理异常

代码实现示例

过滤器实现示例

1. 字符编码过滤器
@WebFilter(urlPatterns = "/*", filterName = "encodingFilter")
@Order(1)
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");
        response.setContentType("text/html;charset=UTF-8");
        
        System.out.println(" 编码过滤器 - 前置处理");
        
        // 继续执行链
        chain.doFilter(request, response);
        
        // 后置处理
        System.out.println(" 编码过滤器 - 后置处理");
    }
}
2. 权限验证过滤器
@WebFilter(urlPatterns = "/admin/*", filterName = "authFilter")
@Order(2)
public class AuthenticationFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // 检查用户是否已登录
        String token = httpRequest.getHeader("Authorization");
        
        if (token == null || !validateToken(token)) {
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.getWriter().write("❌ 未授权访问");
            return; // 不继续执行链
        }
        
        System.out.println(" 权限验证通过");
        // 继续执行
        chain.doFilter(request, response);
    }
    
    private boolean validateToken(String token) {
        // 实际的token验证逻辑
        return "valid-token".equals(token);
    }
}
3. 跨域处理过滤器
@WebFilter(urlPatterns = "/*", filterName = "corsFilter")
@Order(0)
public class CorsFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 设置CORS头
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setHeader("Access-Control-Allow-Methods", 
                              "GET, POST, PUT, DELETE, OPTIONS");
        httpResponse.setHeader("Access-Control-Allow-Headers", 
                              "Content-Type, Authorization");
        httpResponse.setHeader("Access-Control-Max-Age", "3600");
        
        // 处理预检请求
        if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
            httpResponse.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        
        chain.doFilter(request, response);
    }
}

拦截器实现示例

1. 日志拦截器
@Component
public class LoggingInterceptor implements HandlerInterceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        
        logger.info(" 请求开始 - URL: {}, Method: {}, 时间: {}", 
                   request.getRequestURL(), 
                   request.getMethod(), 
                   new Date());
        
        return true; // 继续执行
    }
    
    @Override
    public void postHandle(HttpServletRequest request, 
                         HttpServletResponse response, 
                         Object handler, 
                         ModelAndView modelAndView) throws Exception {
        
        logger.info(" Controller执行完成 - Handler: {}", handler);
        
        // 可以修改ModelAndView
        if (modelAndView != null) {
            modelAndView.addObject("requestTime", new Date());
        }
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        
        logger.info("✅ 请求完成 - 执行时间: {}ms, 状态码: {}", 
                   executeTime, 
                   response.getStatus());
        
        if (ex != null) {
            logger.error("❌ 请求异常: ", ex);
        }
        
        // 清理资源
        request.removeAttribute("startTime");
    }
}
2. 业务权限拦截器
@Component
public class BusinessInterceptor implements HandlerInterceptor {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 检查是否为HandlerMethod
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        
        // 检查是否需要权限验证
        RequireAuth requireAuth = handlerMethod.getMethodAnnotation(RequireAuth.class);
        if (requireAuth == null) {
            return true;
        }
        
        // 从请求中获取用户信息
        String userId = request.getHeader("User-Id");
        if (userId == null) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("❌ 缺少用户信息");
            return false;
        }
        
        // 检查用户是否在缓存中
        String cacheKey = "user:" + userId;
        User user = (User) redisTemplate.opsForValue().get(cacheKey);
        
        if (user == null) {
            // 从数据库查询用户
            user = userService.findById(userId);
            if (user == null) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("❌ 用户不存在");
                return false;
            }
            
            // 缓存用户信息
            redisTemplate.opsForValue().set(cacheKey, user, Duration.ofHours(1));
        }
        
        // 验证用户权限
        if (!user.hasPermission(requireAuth.value())) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("❌ 权限不足");
            return false;
        }
        
        // 将用户信息存储到请求中
        request.setAttribute("currentUser", user);
        logger.info("✅ 用户权限验证通过 - 用户: {}, 权限: {}", 
                   user.getUsername(), requireAuth.value());
        
        return true;
    }
}
3. 自定义权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireAuth {
    String value() default ""; // 所需权限
    boolean admin() default false; // 是否需要管理员权限
}

配置类示例

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private LoggingInterceptor loggingInterceptor;
    
    @Autowired
    private BusinessInterceptor businessInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册日志拦截器,拦截所有请求
        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/static/**", "/error")
                .order(1);
        
        // 注册业务拦截器,只拦截API请求
        registry.addInterceptor(businessInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/public/**", "/api/login")
                .order(2);
    }
    
    // 启用过滤器组件扫描
    @Bean
    public FilterRegistrationBean<EncodingFilter> encodingFilter() {
        FilterRegistrationBean<EncodingFilter> registrationBean = 
            new FilterRegistrationBean<>();
        registrationBean.setFilter(new EncodingFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        registrationBean.setName("encodingFilter");
        return registrationBean;
    }
}

使用场景

过滤器适用场景

  • 字符编码处理: 统一设置请求和响应的字符编码
  • 安全过滤: IP黑白名单、CSRF防护、XSS防护
  • 访问日志: 记录所有HTTP请求信息
  • 数据压缩: 响应数据的Gzip压缩
  • 跨域处理: CORS头设置
  • 性能监控: 请求响应时间统计
  • 内容过滤: 敏感词过滤、内容审查

拦截器适用场景

  • 用户认证: 登录状态检查、Token验证
  • 权限控制: 基于角色的访问控制(RBAC)
  • 性能监控: Controller执行时间统计
  • 操作日志: 业务操作记录、审计日志
  • 主题切换: 动态设置页面主题
  • 限流控制: 接口访问频率限制
  • 多端适配: 根据设备类型返回不同内容

实际应用场景对比

场景 过滤器 拦截器 推荐选择
字符编码 Filter
用户登录验证 ⚠️ Interceptor
权限控制 ⚠️ Interceptor
静态资源处理 Filter
跨域处理 Filter
业务日志 Interceptor
性能监控 都可以

最佳实践

1. 选择原则

// 选择决策树
if (需要处理静态资源) {
    return "使用Filter";
} else if (需要Spring依赖注入) {
    return "使用Interceptor";
} else if (需要在多个时机处理) {
    return "使用Interceptor";
} else if (是底层通用功能) {
    return "使用Filter";
} else {
    return "使用Interceptor";
}

2. 性能优化

// ❌ 避免在过滤器中进行重量级操作
@WebFilter("/*")
public class BadFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        // 避免:数据库查询、复杂计算、网络调用
        User user = userService.complexQuery(); // 性能瓶颈
        chain.doFilter(request, response);
    }
}

// ✅ 正确:轻量级处理
@WebFilter("/*")
public class GoodFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        // 推荐:简单的处理逻辑
        request.setCharacterEncoding("UTF-8");
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        
        try {
            chain.doFilter(request, response);
        } finally {
            // 清理工作
            long duration = System.currentTimeMillis() - startTime;
            if (duration > 1000) { // 超过1秒记录日志
                logger.warn("慢请求: {} - {}ms", 
                          ((HttpServletRequest) request).getRequestURI(), 
                          duration);
            }
        }
    }
}

3. 异常处理

@Component
public class SafeInterceptor implements HandlerInterceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(SafeInterceptor.class);
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        try {
            // 业务逻辑
            return validateRequest(request);
        } catch (BusinessException e) {
            logger.error("业务异常: {}", e.getMessage());
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write(e.getMessage());
            return false;
        } catch (Exception e) {
            logger.error("系统异常", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("系统繁忙,请稍后重试");
            return false;
        }
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        try {
            // 清理资源
            cleanupResources(request);
        } catch (Exception e) {
            logger.error("资源清理失败", e);
        }
    }
    
    private void cleanupResources(HttpServletRequest request) {
        // 清理请求中的临时数据
        request.removeAttribute("tempData");
        request.removeAttribute("startTime");
    }
}

4. 配置管理最佳实践

@Configuration
@EnableConfigurationProperties(WebFilterProperties.class)
public class FilterConfig {
    
    @Autowired
    private WebFilterProperties properties;
    
    @Bean
    @ConditionalOnProperty(name = "app.filter.encoding.enabled", havingValue = "true")
    public FilterRegistrationBean<EncodingFilter> encodingFilter() {
        FilterRegistrationBean<EncodingFilter> registrationBean = 
            new FilterRegistrationBean<>();
        registrationBean.setFilter(new EncodingFilter());
        registrationBean.addUrlPatterns(properties.getEncoding().getUrlPatterns());
        registrationBean.setOrder(properties.getEncoding().getOrder());
        return registrationBean;
    }
    
    @Bean
    @ConditionalOnProperty(name = "app.filter.cors.enabled", havingValue = "true")
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        FilterRegistrationBean<CorsFilter> registrationBean = 
            new FilterRegistrationBean<>();
        registrationBean.setFilter(new CorsFilter());
        registrationBean.addUrlPatterns(properties.getCors().getUrlPatterns());
        registrationBean.setOrder(properties.getCors().getOrder());
        return registrationBean;
    }
}

@ConfigurationProperties(prefix = "app.filter")
@Data
public class WebFilterProperties {
    private EncodingProperties encoding = new EncodingProperties();
    private CorsProperties cors = new CorsProperties();
    
    @Data
    public static class EncodingProperties {
        private boolean enabled = true;
        private String[] urlPatterns = {"/*"};
        private int order = 1;
    }
    
    @Data
    public static class CorsProperties {
        private boolean enabled = false;
        private String[] urlPatterns = {"/*"};
        private int order = 0;
    }
}

5. 监控和调试

@Component
public class MonitoringInterceptor implements HandlerInterceptor {
    
    private final MeterRegistry meterRegistry;
    private final Counter requestCounter;
    private final Timer requestTimer;
    
    public MonitoringInterceptor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.requestCounter = Counter.builder("http.requests")
                .description("HTTP请求计数")
                .register(meterRegistry);
        this.requestTimer = Timer.builder("http.request.duration")
                .description("HTTP请求执行时间")
                .register(meterRegistry);
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        Timer.Sample sample = Timer.start(meterRegistry);
        request.setAttribute("timer.sample", sample);
        
        requestCounter.increment(
            Tags.of(
                "method", request.getMethod(),
                "uri", request.getRequestURI()
            )
        );
        
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        
        Timer.Sample sample = (Timer.Sample) request.getAttribute("timer.sample");
        if (sample != null) {
            sample.stop(requestTimer.tag("status", String.valueOf(response.getStatus())));
        }
    }
}

总结

过滤器和拦截器都是Java Web开发中的重要组件,理解它们的区别和适用场景对于构建高质量的Web应用至关重要:

核心要点

  • 过滤器 更适合处理通用的、底层的请求处理逻辑,如编码、跨域、安全等
  • 拦截器 更适合处理业务相关的、精细化的控制逻辑,如认证、权限、日志等
  • 在实际项目中,两者往往配合使用,形成完整的请求处理链

选择建议

  1. 需要处理所有请求(包括静态资源) → 使用Filter
  2. 只需要处理Controller请求 → 使用Interceptor
  3. 需要Spring依赖注入 → 使用Interceptor
  4. 需要在多个时机进行处理 → 使用Interceptor
  5. 底层通用功能 → 使用Filter
  6. 业务相关功能 → 使用Interceptor

性能考虑

  • Filter执行在容器层面,开销相对较小
  • Interceptor需要经过Spring框架处理,但提供更多功能
  • 合理配置执行顺序,避免重复处理
  • 避免在Filter中进行重量级操作

通过合理使用过滤器和拦截器,可以有效提高代码的可维护性、可扩展性和系统的安全性。


本文详细介绍了Java过滤器与拦截器的核心概念、实现方式、使用场景和最佳实践,希望对您的开发工作有所帮助!

你可能感兴趣的:(Java过滤器与拦截器深度解析)