在 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() 打断点

debug启动,观察调用栈:

可见对 @WebFilter 的处理是在SB启动时,在ServletComponentRegisteringPostProcessor被触发,实现对如下注解的的扫描和处理:

  • @WebFilter
  • @WebListener
  • @WebServlet

WebFilterHandler则负责处理 @WebFilter 的使用:

最后,WebServletHandler 通过父类 ServletComponentHandler 的模版方法模式,处理了所有被 @WebFilter 注解的类:
可见最终注册的 FilterRegistrationBean就是自定义的WebFilter。

看第二个问题:

何时实例化TimeCostFilter

TimeCostFilter 是何时实例化的呢?为什么它没有成为一个普通 Bean?
可在 TimeCostFilter 构造器中加断点,便于快速定位初始化时机:

结合源码,可发现:

  • Tomcat 启动时(onstartUp),才会创建 FilterRegistrationBean
  • FilterRegistrationBean 在被创建时(createBean)会创建 TimeCostFilter 装配自身,而 TimeCostFilter 是通过 ResolveInnerBean 创建的
  • TimeCostFilter 实例最终是一种 InnerBean

所以最终 TimeCostFilter 实例是一种 InnerBean,也就无法自动注入了。

修正

找到根因,就知道如何解决了。

前文解析可知,使用 @WebFilter 修饰过滤器时,TimeCostFilter 类型的 Bean 并没有注册至 Spring 容器,真正注册的是 FilterRegistrationBean。
考虑到还可能存在多个 Filter,可这样修改:

  • 注入 FilterRegistrationBean 类型,而非 TimeCostFilter 类型
  • 注入的名称是包含包名的全限定名,不能直接用 TimeCostFilter,以便存在多个过滤器时,能精确匹配。

总结

@WebFilter 这种方式构建的 Filter 无法直接根据过滤器定义类型自动注入,因为这种Filter本身是以内部Bean呈现,最终是通过FilterRegistrationBean呈现给Spring。
所以可通过自动注入FilterRegistrationBean类型完成自动装配。

为什么加了@WebFilter注解,Spring却没有给我自动注入该过滤器?相关推荐

  1. Spring Aop面向切面编程自动注入

    1.面向切面编程 在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面的过程叫做面向切面编程 2.常用概念 原有功能:切点,pointcut 前置通知:在切点之前执行的功能,befor ...

  2. Spring第8篇:自动注入(autowire)详解

    本文内容 手动注入的不足 Class.isAssignableFrom方法介绍 3种自动注入方式详解及案例 按名称自动注入 按类型自动注入 按构造器进行自动注入 按类型自动注入某种类型的所有bean给 ...

  3. Spring官网阅读(三)自动注入

    上篇文章我们已经学习了1.4小结中关于依赖注入跟方法注入的内容.这篇文章我们继续学习这结中的其他内容,顺便解决下我们上篇文章留下来的一个问题-----注入模型. 前言: 在看下面的内容之前,我们先要对 ...

  4. 从源码层面带你实现一个自动注入注解

    如何自己实现一个自动注入的注解 首先,需要了解到的是.Spring Bean 的生命周期 在生命周期中.注入bean属性的位置是在以下代码:populateBean 位置中 那么我们在项目中使用注解 ...

  5. @autowired注解注入为null_Spring @Autowired 注解自动注入流程是怎么样?

    面试中碰到面试官问:"Spring 注解是如果工作的?",当前我一惊,完了这不触及到我的知识误区了吗?,还好我机智,灵机一动回了句:Spring 注解的工作流程倒还没有看到,但是我 ...

  6. 解决:SpringBoot--获取自动注入属性为空失败(注解无误情况下)

    1.需要初始化的bean 2.把第一步的bean类注入到下面的类 3.打印注入的bean 结果:①kafkaRulePropertiesKafkaRuleProperties(groupId=GROU ...

  7. spring源码分析03-spring依赖注入源码解析

    依赖注入流程图: 1. Spring中有几种依赖注入的方式? 1.1手动注入 在XML中定义Bean时,就是手动注入,因为是程序员手动给某个属性指定了值. 下面这种底层是通过set方法进行注入. &l ...

  8. 网店版重生系列:都是Spring配置中自动注入惹的祸

    在对Spring Container管理的bean进行配置时,有一个很好用的功能就是自动注入,可以根据不同规则对bean所依赖的bean进行自动set,相信最常用的就是设置default-autowi ...

  9. Spring Boot 技术知识点:如何详解@WebFilter注解

    功能说明 在servlet3.0以后,我们可以不用在web.xml文件内配置Filter,只需要加上@WebFilter注解就可以实现,以达到简化配置的目的.该注解用来声明servlet过滤器,将会在 ...

最新文章

  1. 【.Net】vs2017 自带发布工具 ClickOnce发布包遇到的问题
  2. CDQ分治 + 树状数组 ---- C. Goodbye Souvenir(三维偏序+思维)
  3. Python入门学习---第三天
  4. 计算机应用基础本科常见问题讨论,《计算机应用基础》(本科)2017年6月期末考试指导.pdf...
  5. pandas基础(part5)--透视表与交叉表
  6. Android动态布局
  7. libcudart.so.8.0 cannot open shared object file: No such file or directory
  8. 开课吧:现阶段人工智能应用涉及到哪些行业?
  9. python range 和 xrange 区别
  10. mac新手入门:从启动台Launchpad中完全删除应用程序
  11. 利用Wireshark分析UDP数据包
  12. 4.2 制定项目章程
  13. 5步完成物联网小程序开发
  14. ad走线打过孔_Altium Designer规则设计技巧过孔和焊盘
  15. 高德地图加载不出来,有高德的logo,但地图一片空白
  16. 南通java行业,南通java技术培训中心
  17. CSS的浮动属性,详细学习指南
  18. code first修改表或字段
  19. 电脑C盘满了有什么影响?如何正确清理C盘?
  20. [分享]蓝屏代码查询及代码分析

热门文章

  1. linux C语言实现文件锁之flock
  2. python第五章课后题答案超星_Python数据分析与数据可视化章节考试题库
  3. 5. 项目管理之范围管理
  4. 微电网和直流电网中最优潮流(OPF)的凸优化(Matlab代码实现)
  5. 北风网web前端开发培训课程 web前端开发实例视频教程下载
  6. singalrhub
  7. python计算2的n次方编写_Python计算一元函数的N次方多项式
  8. 韬睿Toradex colibri IMX6开发板的WinCE系统开发
  9. 【运维篇】运维知识点
  10. MATLAB GUI界面编程——一些细节问题