本文最后更新于 130 天前 ,文中信息可能已经过时。如有问题请在评论区留言。

文中代码基于 Java 17

Spring 拦截器与 Servlet Filter 的异同

Spring 的拦截器(Interceptor)与 Servlet 的 Filter 有相似之处,比如二者都是 AOP 编程思想的体现,都能实现权限检查、日志记录等。

不同的是:

  • 使用范围不同:Filter 是 Servlet 规范规定的,只能用于 Web 程序中。而拦截器既可以用于 Web 程序,也可以用于 Application、Swing 等程序中。
  • 规范不同:Filter 是 Servlet 规范中定义的,是 Servlet 容器支持的。而拦截器是在 Spring 容器内的,是 Spring 框架支持的。
  • 使用资源不同:同其他代码块一样,Interceptor 也是一个 Spring 的组件,归 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过 IoC 注入拦截器即可。而 Filter 则不能。
  • 深度不同:Filter 只在 Servlet 前后起作用。而拦截器能够深入到方法前后、异常抛出前后等。因此拦截器的使用具有更大的弹性。所以在 Spring 架构的程序中,要优先使用拦截器。

对于一个请求,拦截器、过滤器执行流程如下:

flowchart LR
  A((request))
  B(Filter)
  C(Servlet)
  D(Interceptor)
  E(Controller)
  F((doService))
  
  A --> B --> C --> D --> E --> F

何时使用 Filter、Interceptor?

  • 如果是非 Spring 项目,那么拦截器不能用,只能使用过滤器。
  • 如果是处理 Controller 前后,既可以使用拦截器也可以使用过滤器。
  • 如果是处理 DispatcherServlet 前后,只能使用过滤器。

Spring Boot 使用 Filter

Spring Boot 使用 Filter 有两种方式:

  • 使用 Spring Boot 提供的 FilterRegistrationBean 注册 Filter
  • 使用原生 Servlet 注解 @WebFilter 定义 Filter

使用 FilterRegistrationBean 注册 Filter

  1. 自定义 Filter 并实现 jakarta.servlet.Filter 。(低版本 JDK 请使用 javax.servlet.Filter
java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // your code
    }
}
  1. 自定义配置类配置 Filter
java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfig {
    
    @Bean
    public FilterRegistrationBean<MyFilter> myFilterFilterRegistration() {
        FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
        MyFilter myFilter = new MyFilter();
        registrationBean.setFilter(myFilter);
        return registrationBean;
    }
}

使用原生 Servlet 注解 @WebFilter 定义 Filter

  1. 自定义 Filter 并实现 jakarta.servlet.Filter (低版本 JDK 请使用 javax.servlet.Filter),同时添加 @WebFilter 注解。
java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;

@WebFilter
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // your code
    }
}
  1. 在启动类上添加 @ServletComponentScan 注解
java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;


@SpringBootApplication
@ServletComponentScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

自定义过滤器执行顺序

使用自定义配置类配置过滤器时,可通过 setOrder() 方法设置过滤器执行顺序。如不设置,则按照 Spring Boot Bean 加载顺序。

使用 Servlet 原生注解 @WebFilter 时,只能通过限定 Filter 类型(按字母表 A - Z 的顺序)。注意:此方式使用 @Order 注解无效。

Spring Boot 使用 Interceptor

当请求来到 DispatcherServlet 时,它会根据 HandlerMapping 的机制找到处理器,这样就会返回一个 HandlerExecutionChain 对象。这个对象包含处理器和拦截器。这里的拦截器会对处理器进行拦截,这样通过拦截器就可以增强处理器的功能。 ——《深入浅出 Spring Boot 2.X》- 杨开振

拦截器的设计(Interceptor 接口)

所有的拦截器都需要实现 HandlerInterceptor 接口。该接口主要定义如下:

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

这些方法的执行流程如下:

  1. 执行 preHandler() 方法。该方法会返回一个布尔值。如果为 false ,则结束所有流程;如果为 true ,则执行下一步
  2. 执行处理器逻辑。它包含控制器的功能。
  3. 执行 postHandle() 方法。
  4. 执行视图解析和视图渲染。
  5. 执行 afterCompletion() 方法。

开发拦截器

定义简单拦截器

实现 HandlerInterceptor 接口,并实现其方法:

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("处理器执行前方法");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("处理器执行后方法");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("处理器完成方法");
    }
}

注册拦截器

新建配置类并实现 WebMvcConfigurer 接口,重写 addInterceptors(InterceptorRegistry registry) 方法:

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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("/test/*");
    }
}

多个拦截器顺序

多个拦截器拦截同一路径时,采用责任链模式的规则,对于处理器前方法采用先注册先执行,而处理后方法和完成方法则是先注册后执行的规则。

当多个拦截器中某一个处理前(preHandle())方法为 false 时,则后续拦截器、处理器和所有拦截器的处理器后( postHandle() )方法都不会执行。完成方法( afterCompletion() )则不一样,它只会执行返回 true 的拦截器的完成方法,而且顺序是先注册后执行