过滤器执行顺序问题分析

最近碰到了一个关于过滤器的奇怪的问题。

问题描述

项目中定义了若干过滤器,其中一个是解析header参数,并做相应处理。另一个过滤器覆写了getHeader方法,判断参数值中是否有非法字符,如果有则抛出异常。这两个过滤器均通过WebFilter + ServletComponetScan注解方式来声明的。

在写本文之前,我认为WebFilter方式声明的过滤器是按照类名的字母表顺序来执行的,本地执行结果和网上的很多资料都显示是这样。
而这两个过滤器类名按照字母表顺序,覆写getHeader方法的过滤器执行在前,解析header参数的过滤器执行在后,这样当解析header参数调用request.getHeader方法时,就会执行覆写的方法了。

实际上本地执行结果也符合这一认知,但是在k8s集群中执行时问题就来了,从返回结果看并没有进入到覆写方法中,开启远程调试也显示没有进入到覆写方法过滤器中,在过滤器中加上日志后又一切正常。这个bug好像是变成了量子态一样不可观测,找了很久都找不到原因。

但其表现总归是覆写方法的过滤器没有执行到,因此直接更换过滤器注册方式。最后问题不再出现。

根据查询到的资料发现,其实WebFilter声明的过滤器执行是没有明确的顺序的,执行顺序不可控

很多资料都说到了这一点,但是没有给出令人信服的原因。参考资料2中倒是有具体分析,说是通过扫描加载过滤器类文件的时候调用了File类的list方法,该方法不保证文件的有序性。

* <p> There is no guarantee that the name strings in the resulting array
* will appear in any specific order; they are not, in particular,
* guaranteed to appear in alphabetical order.

这样问题就能解释得通了。通过WebFilter注册的过滤器执行顺序不

### Java中拦截器和过滤器执行顺序及其区别 在Java Web开发中,**过滤器(Filter)** 和 **拦截器(Interceptor)** 是两个重要的概念,它们都用于拦截HTTP请求并对请求或响应进行预处理或后处理。然而,两者的运行机制、配置方式以及执行顺序存在显著差异。 #### 1. 过滤器与拦截器的主要区别 - **运行环境** - 过滤器是由Servlet容器管理的标准组件,适用于任何基于Servlet的应用程序[^3]。 - 拦截器则由Spring框架提供支持,主要用于Spring MVC生态系统内的请求处理。 - **配置方式** - 过滤器通常通过`web.xml`文件或使用`@WebFilter`注解来声明[^3]。 - 拦截器需要在Spring的配置类中显式注册,例如通过实现`WebMvcConfigurer`接口并调用`addInterceptors()`方法[^3]。 - **功能范围** - 过滤器仅能操作`HttpServletRequest`和`HttpServletResponse`对象。 - 拦截器不仅可以操作这些标准对象,还可以访问Spring MVC特有的组件,如`Handler`实例和`ModelAndView`对象。 #### 2. 执行顺序分析 根据已有资料[^2],过滤器和拦截器的执行顺序遵循以下规律: - 当一个HTTP请求到达服务器时,首先会经过过滤器中的各个过滤器。 - 接着,在进入目标控制器之前,拦截器的`preHandle()`方法会被调用。 - 响应生成之后,拦截器的`postHandle()`方法会在视图渲染完成前被调用。 - 最终,在整个请求结束且响应发送回客户端之前,拦截器的`afterCompletion()`方法被执行,随后控制权交还给过滤器以完成后续清理工作。 具体来说,典型的执行流程如下: 1. Filter Chain -> doFilter() 2. Interceptor -> preHandle() 3. Controller Logic Execution 4. Interceptor -> postHandle() 5. View Rendering (if applicable) 6. Interceptor -> afterCompletion() 7. Final Response Sent Back to Client via Filters[^2] #### 3. 示例代码展示 ##### (1)过滤器示例 ```java import javax.servlet.*; import java.io.IOException; @WebFilter(filterName = "myFilter", urlPatterns = {"/*"}) public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Before filtering..."); chain.doFilter(request, response); System.out.println("After filtering..."); } @Override public void destroy() {} } ``` ##### (2)拦截器示例 ```java import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Pre-handle method called"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("Post-handle method called"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("After-completion method called"); } } ``` ##### (3)测试结果说明 假设我们有这样一个简单的API端点: ```java @RestController @RequestMapping("/example") public class ExampleController { @GetMapping("/hello") public String sayHello() { return "Hello from controller!"; } } ``` 当发出GET请求至`/example/hello`时,终端输出可能类似于这样: ``` Before filtering... Pre-handle method called [Controller logic executed here] Post-handle method called After-completion method called After filtering... ``` 这表明了各部分按照预期顺序依次触发。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值