在 Spring 编程中,主要配合如下注解构建过滤器:
- @ServletComponentScan
- @WebFilter
那这看起来只是用上这俩注解就能继续摸鱼了呀。但上了生产后,还是能遇到花式问题:
- 工作不起来
- 顺序不对
- 执行多次等
大多因为想当然觉得使用简单,没有上心。还是有必要精通过滤器执行的流程和原理。
@WebFilter 过滤器无法被自动注入
为统计接口耗时,实现一个过滤器:
该过滤器标记了 @WebFilter。所以启动程序加上扫描注解 @ServletComponentScan 让其生效:
然后,提供一个 UserController:
发现应用启动失败
TimeCostFilter 看起来是个普通 Bean啊,为何不能被自动注入?
源码解析
本质上,过滤器被 @WebFilter 修饰后,TimeCostFilter 只会被包装为 FilterRegistrationBean,而 TimeCostFilter 本身只会作为一个 InnerBean 被实例化,这意味着 TimeCostFilter 实例并不会作为 Bean 注册到 Spring 容器。
所以当我们想自动注入 TimeCostFilter 时,就会失败。知道这个结论后,我们可以带着两个问题去理清一些关键的逻辑:
FilterRegistrationBean 是什么?它是如何被定义的
javax.servlet.annotation.WebFilter
所以它不属 Spring,而是 Servlet 规范。
Spring Boot 项目使用它时,Spring Boot 使用了 org.springframework.boot.web.servlet.FilterRegistrationBean 包装 @WebFilter 标记的实例。
实现上来说,即 FilterRegistrationBean#Filter 属性就是 @WebFilter 标记的实例。这点我们可以从之前给出的截图中看出端倪。
定义一个 Filter 类时,我们可能想的是,会自动生成它的实例,然后以 Filter 的名称作为 Bean 名来指向它。
但调试发现,在 Spring Boot 中,Bean 名字确实是对的,只是 Bean 实例其实是 FilterRegistrationBean。
这 FilterRegistrationBean 最早是如何获取的呢?
得追溯到 @WebFilter 注解是如何被处理的。
@WebFilter 是如何工作的
使用 @WebFilter 时,Filter 被加载有两个条件:
- 声明了 @WebFilter
- 在能被 @ServletComponentScan 扫到的路径下
直接搜索对 @WebFilter 的使用,可发现 WebFilterHandler 使用了它:
因此,我们选择在 doHandle() 打断点







