Filter、Listener、Interceptor 的区别与联系
2025-01-19 09:46:49 # Technical # SpringMVC

Filter、Listener 和 Interceptor 是用于处理 Web 应用中请求和响应的不同层面的组件,它们具有不同的职责和用途,但在某些情况下可以协同工作

Filter(过滤器)

职责

Filter(过滤器),是 JavaEE 的标准,依赖于 Servlet 容器,Filter 用于在请求进入 Servlet 容器之前和响应离开 Servlet 容器之前执行特定的任务,如请求参数解析、字符编码处理、日志记录、权限验证等。Filter 可以修改请求和响应,也可以拦截请求或响应,甚至终止请求的传递

配置

Filter 通过在 web.xml 文件中配置和声明,并通过 FilterChain 连接多个过滤器,按照配置的顺序执行

创建一个 MyFilter 类,实现 javax.servlet.Filter 接口,重写 doFilter 方法

1
2
3
4
5
6
7
8
9
10
11
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Filter 的逻辑代码
chain.doFilter(request, response); // 继续请求的传递
// 可以在此处理响应
}

// 其他 Filter 方法
}

web.xml 中配置 Filter, 包括 Filter 的名称、Filter 类的完全限定名以及初始化参数(如果需要)

1
2
3
4
5
6
7
8
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
</filter>

配置 Filter 的映射,指定哪些 URL 或 Servlet 需要经过这个 Filter 的处理

1
2
3
4
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/example/*</url-pattern>
</filter-mapping>

或者使用 Tomcat 的 @WebFilter 注解

1
2
3
4
5
6
7
8
9
10
11
@WebFilter("/example")
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 在这里编写Filter的逻辑
// 你可以在适当的时机使用chain.doFilter方法继续请求的传递
chain.doFilter(request, response);
// 在这里可以处理响应
}
}

如果有多个 @WebFilter 注解定义多个过滤器时,过滤器的执行顺序是按照它们在类路径中的加载顺序来确定的

或者实现 Filter 接口然后通过 FilterRegistrationBean 注入

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Filter 的逻辑代码
chain.doFilter(request, response); // 继续请求的传递
// 可以在此处理响应
}

// 其他 Filter 方法
}

这里需要使用 @Component 注解将 MyFilter 注册为 Bean

接着注入 MyFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class FilterConfig {

private final MyFilter myFilter;

public FilterConfiguration(MyFilter myFilter) {
this.myFilter = myFilter;
}

@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(myFilter);
registration.addUrlPatterns("/*");
registration.setOrder(1); // 值越小越靠前
return registration;
}
}

应用场景

  • 用户授权的 Filter:Filter 负责检查用户请求,根据请求过滤用户非法请求
  • 日志 Filter:详细记录某些特殊的用户请求
  • 负责解码的 Filter:包括对非标准编码的请求解码
  • 能改变 XML 内容的 XSLT Filter 等
  • Filter 可以负责拦截多个请求或响应,一个请求或响应也可以被多个 Filter 拦截

适用范围

Filter 作用于整个请求生命周期,它不依赖于具体的处理器(Controller)

Listener(监听器)

职责

Listener 用于监听 Web 应用中的事件,具体有:

  • ServletContextListener 监听 Servlet 整个上下文的创建、销毁
  • HttpSessionListener 监听客户端会话的创建和销毁
  • ServletRequestListener 监听客户端请求的初始化与销毁
  • Web 容器特定的 Listener,如 Tomcat 的 LifecycleListener 监听 Tomcat 的启动、停止、初始化、销毁等事件

配置

注意:使用 @WebListener 注解前,需在启动类添加 @ServletComponentScan 注解,以确保 Servlet 相关组件(@WebServlet、@WebFilter、@WebListener)被扫描并注册

配置 ServletContextListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
log.debug("Web Context Initialized");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
log.debug("Web Context Destroyed");
}
}

配置 HttpSessionListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Slf4j
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent se) {
log.debug("create session: {}", se.getSession().getId());
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
log.debug("destroy session: {}", se.getSession().getId());
}
}

配置 ServletRequestListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Slf4j
@WebListener
public class MyServletRequestListener implements ServletRequestListener {

@Override
public void requestDestroyed(ServletRequestEvent sre) {
log.debug("Request Destroyed");
}

@Override
public void requestInitialized(ServletRequestEvent sre) {
log.debug("Request Initialized");
}
}

配置 LifecycleListener

1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j
public class MyLifecycleListener implements LifecycleListener {

@Override
public void lifecycleEvent(LifecycleEvent lifecycleEvent) {
if (lifecycleEvent.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
log.debug("Tomcat 启动前...");
} else if (lifecycleEvent.getType().equals(Lifecycle.AFTER_STOP_EVENT)) {
log.debug("Tomcat 停止后...");
}
}
}

注册 LifecycleListener

1
2
3
4
5
<Server>
...
<Listener className="com.example.MyCustomLifecycleListener" />
...
</Server>

应用场景

  • ServletContextListener 可以在应用启动时设置全局参数,创建数据库连接池等
  • HttpSessionListener 用户监听客户端会话的创建和销毁,可以统计在线用户数,设置会话参数等
  • ServletRequestListener 用于监听客户端请求,可以跟踪用户访问轨迹,设置请求参数等
  • Web 容器的 Listener 可以在容器启动时初始化数据库连接池(跨 app),也可以获取容器实例,动态加载 APP

适用范围

Listener 通常用于监听整个 Web 应用的事件,而不是单个请求

Interceptor(拦截器)

职责

Interceptor 用于拦截请求的处理,它的职责和 Filter 很类似,它主要用于对控制器方法的调用进行拦截,允许在请求处理前和处理后执行一些自定义逻辑,如日志记录、权限验证、处理异常等

配置

这里以前面 实践 Session、Cookie、JWT、Token、Sa-Token 中的 SessionInterceptor 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Slf4j
public class SessionInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
log.info("拦截请求:{}", uri);
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
log.info("cookie:{}", cookie.getName());
if (Objects.equals(cookie.getName(), "userId")) {
String userId = cookie.getValue();
User user = (User) request.getSession().getAttribute(userId);
if (Objects.isNull(user)) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 401);
jsonObject.put("msg", "重新登录");
returnJson(response, jsonObject);
return false;
}
log.info("{}:已登录", user.getUserName());
return true;
}
}
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 401);
jsonObject.put("msg", "未登录");
returnJson(response, jsonObject);
return false;
}

private void returnJson(HttpServletResponse response, JSONObject jsonObject) {
//将实体对象转换为JSON Object转换
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try (PrintWriter out = response.getWriter()) {
out.append(jsonObject.toJSONString());
} catch (IOException e) {
log.error(e.getMessage(), e);
}

}
}

注册拦截器

1
2
3
4
5
6
7
8
9
10
@Configuration
public class WebMvcAuthConfig extends WebMvcConfigurerAdapter {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SessionInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/captcha/**");
}
}

WebMvcConfigurerAdapter 在 Spring 5.0 版本中被弃用,建议使用新的方式来进行 Spring MVC 的配置,可以实现 WebMvcConfigurer 接口并重写其中的方法

应用场景

  • 身份验证和授权:就如前面所使用到的, 拦截器用于检查用户的身份验证和授权信息。这可以包括检查用户是否已登录,用户是否有足够的权限来访问特定页面或执行特定操作。身份验证和授权拦截器可以保护需要受限访问的资源
  • 日志和监控: 拦截器用于记录请求和响应的详细信息,以进行日志记录和性能监控。这有助于跟踪应用程序的行为,排查问题和优化性能
  • 数据预处理: 拦截器可以在请求到达控制器之前对请求数据进行预处理。这可以包括数据验证、数据格式化、请求参数解析等操作
  • 全局异常处理: 拦截器可用于捕获控制器和服务方法中抛出的异常,然后采取适当的措施,如返回错误响应、记录异常信息或执行特定的异常处理逻辑
  • 缓存控制: 拦截器可以用于控制缓存的行为。例如,它可以根据请求参数或响应头来设置缓存的策略,如缓存过期时间或缓存无效ation
  • 跨域资源共享 (CORS): 拦截器可用于处理跨域请求,如添加必要的响应头以允许跨域资源共享
  • 国际化和本地化: 拦截器可用于检测用户的首选语言,并相应地选择合适的国际化资源或视图
  • 请求转发和重定向: 拦截器可以根据条件将请求重定向到不同的 URL 或控制器方法,或者将请求转发到其他位置
  • 安全性增强: 拦截器可以增强应用程序的安全性,例如,通过防止跨站请求伪造(CSRF)攻击、点击劫持等
  • 定制响应: 拦截器可以用于在请求处理后修改响应,例如,添加响应头、修改响应内容或执行其他响应处理逻辑

适用范围

Interceptor 作用于 Spring MVC 框架的请求处理过程,只对控制器方法的调用进行拦截

Filter 与 Interceptor

Filter 和 Interceptor 很相似,那么他们之间有什么异同呢

Filter Interceptor Summary
Filter 接口定义在 javax.servlet 包中 接口 HandlerInterceptor 定义在org.springframework.web.servlet 包中 本质不同
Filter 配置在 web.xml 中,或者 @WebFilter 或者 FilterRegistrationBean 注入 通过实现 WebMvcConfigurer 或者 WebMvcConfigurerAdapter 注入 配置方式不同
Filter 在只在 Servlet 前后起作用。Filters 通常将 请求和响应(request/response) 当做黑盒子,Filter 通常不考虑 servlet 的实现 Interceptor 能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入(hook into)请求的生命周期,在请求过程中获取信息,Interceptor 通常和请求更加耦合 在 Spring 构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情,interceptor 都能够轻松的实现
Filter 是 Servlet 规范规定的 Interceptor 是在 Spring MVC 内的,是 Spring 框架支持的 规范不同
Filter 不能够使用 Spring 容器资源(除非将 Filter 交由 Spring 管理) Interceptor 是一个 Spring MVC 的机制,由 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过 IoC 注入到拦截器即可 Spring 中使用 interceptor 更容易
Filter 是被 Servlet (Tomcat等) 调用 Interceptor 是被 Spring MVC 调用 因此 Filter 总是优先于 Interceptor 执行
Filter 由 Servlet 容器维护在一个过滤器链(Filter Chain)中,Servlet 容器在启动时会扫描和加载应用程序中配置的过滤器,并在请求到达时直接调用它们的 doFilter 方法 Interceptor 由 InterceptorRegistry 注册到 Spring MVC 维护的拦截器链中,在请求处理前,拦截器链会按照注册顺序依次调用每个拦截器的 preHandle 方法 实现方式不同

Thanks

过滤器(Filter)和拦截器(Interceptor)的执行顺序和区别

全栈 | 如何在微服务或Tomcat中配置使用Listener