[springboot系列] 过滤器和拦截器

介绍

过滤器(Filter)是 Servlet 规范中的组件,运行在 Servlet 容器中,可对所有 HTTP 请求进行预处理和后处理(如编码设置、跨域配置),依赖Servlet API且执行时机最早;拦截器(Interceptor)是 Spring MVC 框架的组件,仅拦截 Controller 层请求,可获取 HandlerMethod 信息(如方法参数、注解),适合权限验证、日志记录等业务层面的拦截。

如上图所示(图片来自网络),在基于 Tomcat 和 Servlet 的 Web 应用架构中,过滤器(Filter)与拦截器(Interceptor)虽均承担请求处理的拦截与增强职责,但其作用范围与执行阶段存在显著差异。从架构层面来看,Tomcat 作为 Servlet 容器,是整个 Web 应用的运行载体,而 Spring MVC 应用本质上是基于 Servlet 规范构建的上层框架。

  • 过滤器处于 Tomcat 容器与 Servlet 应用之间,在请求进入 Servlet 前或响应离开 Servlet 后执行,能够对所有流经 Tomcat 的 HTTP 请求进行拦截处理,包括静态资源请求和动态业务请求,适合处理跨域配置、字符编码转换、请求日志记录等与 Servlet 容器紧密相关的通用逻辑。
  • 拦截器则深度嵌入 Spring MVC 框架内部,仅对进入 Servlet 后的 Controller 层请求生效(动态业务请求),可获取方法注解、参数等 Spring MVC 特有的上下文信息,更适用于权限校验、业务逻辑前置处理、性能监控等聚焦于 Spring 应用内部业务逻辑的场景。


过滤器 Filter

实现 Filter 接口

要编写一个自定义的 Filter,可以实现 javax.servlet.Filter 接口,并重写该接口中的三个方法。具体来说,这三个方法分别是:

  • init(FilterConfig filterConfig),它在过滤器初始化时被调用,通常用于加载初始化参数或进行其他配置操作,只执行一次。如果没有特别的初始化需求,可以留空。
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain),这是过滤器的核心方法,用于执行具体的请求过滤逻辑。你可以在这个方法中检查请求、修改响应,甚至决定是否继续执行过滤链。如果调用了 chain.doFilter(request, response),请求会继续传递到下一个过滤器或目标 Servlet,如果不调用则会阻止请求继续处理。
  • destroy() ,它在过滤器销毁时被调用,通常用于释放占用的资源,只执行一次。
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

@Component
public class MyFilter implements Filter {
    @Autowired
    private MyService myservice;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init MyFilter");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter before" + myservice.call());
        chain.doFilter(request, response);
        System.out.println("doFilter after");
    }

    @Override
    public void destroy() {
        System.out.println("destroy MyFilter");
    }
}

继承 OncePerRequestFilter (推荐)

OncePerRequestFilter 是 Spring Framework 提供的一个过滤器基类,继承自 javax.servlet.Filter 接口,确保每个 HTTP 请求的生命周期内过滤器只执行一次。它通过调用 doFilterInternal() 方法来执行过滤逻辑,并自动避免请求在转发或重定向过程中重复执行过滤器,从而简化了过滤器的实现,避免了冗余操作。OncePerRequestFilter 适用于一些需要避免重复执行的操作,例如日志记录、身份验证等。

import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class MyFilter extends OncePerRequestFilter {
    @Autowired
    private MyService myservice;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("doFilter before" + myservice.call());
        filterChain.doFilter(request, response);
        System.out.println("doFilter after");
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        // 只对 /hello/* 请求生效
        return !request.getRequestURI().startsWith("/hello");
    }
}


拦截器

在 Spring Boot 中,拦截器需要实现 HandlerInterceptor 接口,并重写三个方法:

  • preHandle():请求到达处理方法之前执行,返回 true 表示继续处理请求,返回 false 表示请求被拦截,后续流程停止。
  • postHandle():请求处理方法执行后,在视图渲染之前执行,此时可以修改视图对象 ModelAndView。
  • afterCompletion():请求处理完成后的回调,即渲染视图完成后,主要用于清理资源。
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Before handler - Request URI: " + request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("After handler - Processing request");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("After request completion");
    }
}

在 Spring Boot 中,我们可以通过实现 WebMvcConfigurer 接口来设置和注册多个拦截器到 Spring 容器中。WebMvcConfigurer 提供了 addInterceptors 方法,允许我们配置多个拦截器并为每个拦截器设置特定的 URL 模式、排除路径和拦截器执行顺序。

默认情况下,拦截器的执行顺序是按照在 InterceptorRegistry 中的注册顺序决定的 (先注册先执行),但使用 order() 方法,你可以通过显式设置每个拦截器的执行顺序来确保拦截器按照你希望的顺序执行 (数字越小先执行)。

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 MyInterceptor())
                .addPathPatterns("/hello/**")  // 设置拦截路径
                .excludePathPatterns("/api/login", "/api/register") // 设置排除的路径
                .order(1);  // 设置优先级

        registry.addInterceptor(new SecondInterceptor())
                .addPathPatterns("/world/**")  // 设置拦截路径
                .excludePathPatterns("/api/login", "/api/register") // 设置排除的路径
                .order(2);  // 设置优先级
    }
}


参考

HandlerInterceptor (Spring Framework 6.2.8 API)

OncePerRequestFilter (Spring Framework 6.2.8 API)

Spring Boot中,过滤器(Filter)拦截器(Interceptor)都可以用于对HTTP请求进行预处理或后处理,但它们的使用场景应用方式有所不同。 **过滤器(Filter)**: 过滤器主要用于在Web层处理特定的请求,如添加或修改请求头、处理预请求逻辑等。在Spring Boot中,你需要在`WebMvcConfigurerAdapter`或自定义的`WebMvcConfigurer`接口中配置filter。例如: ```java import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; public class MyFilterConfig implements WebMvcConfigurer { @Override public void addFilters(FilterRegistrationBean<?> filters) { FilterRegistrationBean<MyFilter> filter = new FilterRegistrationBean<>(); filter.setFilter(new MyFilter()); // 设置过滤器顺序(数字越小优先级越高) filter.setOrder(Ordered.HIGHEST_PRECEDENCE); filters.addFilter(filter); } } ``` 在这里,`MyFilter`是你需要自定义的过滤器类。 **拦截器(Interceptor)**: 拦截器则更适合于处理业务逻辑层面的操作,如权限验证、日志记录等。在Spring MVC中,你可以通过`@ControllerAdvice`注解创建全局拦截器,或者在每个控制器上单独声明。例如: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; @RestController public class MyGlobalInterceptor { private final MyBusinessLogic logic; @Autowired public MyGlobalInterceptor(MyBusinessLogic logic) { this.logic = logic; } @Around("execution(* com.example.controller.*.*(..))") // 匹配所有控制器方法 public Object around(HandlerMethod handlerMethod) throws Exception { // 先进行预处理操作... Object result = handlerMethod.proceed(); // 调用实际方法 // 后处理操作... return result; } } // 或者在单个控制器上使用 @RestController public class MySpecificInterceptorController { @ModelAttribute("myInterceptor") public MySpecificInterceptor interceptor() { return new MySpecificInterceptor(); } // 每个方法都会经过这个拦截器 @PostMapping("/someEndpoint") @PreAuthorize("hasRole('ADMIN')") public ResponseEntity<String> handleRequest(@ModelAttribute("myInterceptor") MySpecificInterceptor interceptor) { // ... } } ``` 这里,`MyBusinessLogic``MySpecificInterceptor`是你的业务逻辑类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值