前面一篇博文介绍了在SpringBoot中使用Filter的两种使用方式,这里介绍另外一种直接将Filter当做Spring的Bean来使用的方式,并且在这种使用方式下,Filter的优先级可以直接通过@Order
注解来指定;最后将从源码的角度分析一下两种不同的使用方式下,为什么@Order
注解一个生效,一个不生效
本篇博文强烈推荐与上一篇关联阅读,可以get到更多的知识点: 191016-SpringBoot系列教程web篇之过滤器Filter使用指南
I. Filter
本篇博文的工程执行的环境依然是SpringBoot2+
, 项目源码可以在文章最后面get
1. 使用姿势
前面一篇博文,介绍了两种使用姿势,下面简单介绍一下
WebFilter注解
在Filter类上添加注解@WebFilter
;然后再项目中,显示声明@ServletComponentScan
,开启Servlet的组件扫描
1 |
|
FilterRegistrationBean
另外一种方式则是直接创建一个Filter的注册Bean,内部持有Filter的实例;在SpringBoot中,初始化的是Filter的包装Bean就是这个
1 |
|
本篇将介绍另外一种方式,直接将Filter当做普通的Bean对象来使用,也就是说,我们直接在Filter类上添加注解@Component
即可,然后Spring会将实现Filter接口的Bean当做过滤器来注册
而且这种使用姿势下,Filter的优先级可以通过@Order
注解来指定;
设计一个case,定义两个Filter(ReqFilter
和OrderFilter
), 当不指定优先级时,根据名字来,OrderFilter优先级会更高;我们主动设置下,希望ReqFilter
优先级更高
1 | 1) ( |
2. 优先级测试
上面两个Filter直接当做了Bean来写入,我们写一个简单的rest服务来测试一下
1 |
|
请求之后输出结果如下, ReqFilter优先执行了
II. 源码分析
当我们直接将Filter当做Spring Bean来使用时,@Order
注解来指定Filter的优先级没有问题;但是前面一篇博文中演示的@WebFilter
注解的方式,则并不会生效
- 这两种方式的区别是什么?
@Order
注解到底有什么用,该怎么用
1. Bean方式
首先我们分析一下将Filter当做Spring bean的使用方式,我们的目标放在Filter的注册逻辑上
第一步将目标放在: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
下面的逻辑中包括了ServeltContext的初始化,而我们的Filter则可以看成是属于Servlet的Bean
1 | private void selfInitialize(ServletContext servletContext) throws ServletException { |
注意上面代码中的for循环,在执行getServletContextInitializerBeans()
的时候,Filter就已经注册完毕,所以我们需要再深入进去
将目标集中在org.springframework.boot.web.servlet.ServletContextInitializerBeans#ServletContextInitializerBeans
1 | public ServletContextInitializerBeans(ListableBeanFactory beanFactory) { |
上面有两行代码比较突出,下面单独捞出来了,需要我们重点关注
1 | addServletContextInitializerBeans(beanFactory); |
通过断点进来,发现第一个方法只是注册了dispatcherServletRegistration
;接下来重点看第二个
1 | "unchecked") ( |
从上面调用的方法命名就可以看出,我们的Filter注册就在addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
上面的截图就比较核心了,在创建FilterRegistrationBean
的时候,根据Filter的顺序来指定最终的优先级
然后再回到构造方法中,根据order进行排序, 最终确定Filter的优先级
2. WebFilter方式
接下来我们看一下WebFilter方式为什么不生效,在根据我的项目源码进行测试的时候,请将需要修改一下自定义的Filter,将类上的@WebFilter
注解打开,@Component
注解删除,并且打开Application类上的ServletComponentScan
我们这里debug的路径和上面的差别不大,重点关注下面ServletContextInitializerBeans
的构造方法上面
当我们深入addServletContextInitializerBeans(beanFactory);
这一行进去debug的时候,会发现我们自定义的Filter是在这里面完成初始化的;而前面的使用方式,则是在addAdapterBeans()
方法中初始化的,如下图
在getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)
的调用中就返回了我们自定义的Bean,也就是说我们自定义的Filter被认为是ServletContextInitializer
的类型了
然后我们换个目标,看一下ReqFilter在注册的时候是怎样的
关键代码: org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
(因为bean很多,所以我们可以加上条件断点)
通过断点调试,可以知道我们的自定义Filter是通过
WebFilterHandler
类扫描注册的, 对这一块管兴趣的可以深入看一下org.springframework.boot.web.servlet.ServletComponentRegisteringPostProcessor#scanPackage
上面只是声明了Bean的注册信息,但是还没有具体的实例化,接下来我们回到前面的进程,看一下Filter的实例过程
1 | private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type, Set<?> excludes) { |
注意我们的Filter实例在T bean = beanFactory.getBean(name, type);
通过这种方式获取的Filter实例,并不会将ReqFilter类上的Order注解的值,来更新FilterRegistrationBean
的order属性,所以这个注解不会生效
最后我们再看一下,通过WebFilter的方式,容器类不会存在ReqFilter.class
类型的Bean, 这个与前面的方式不同
III. 小结
本文主要介绍了另外一种Filter的使用姿势,将Filter当做普通的Spring Bean对象进行注册,这种场景下,可以直接使用@Order
注解来指定Filter的优先级
但是,这种方式下,我们的Filter的很多基本属性不太好设置,一个方案是参考SpringBoot提供的一些Fitler的写法,在Filter内部来实现相关逻辑
0. 项目
web系列博文
- 191016-SpringBoot系列教程web篇之过滤器Filter使用指南
- 191012-SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver
- 191010-SpringBoot系列教程web篇之全局异常处理
- 190930-SpringBoot系列教程web篇之404、500异常页面配置
- 190929-SpringBoot系列教程web篇之重定向
- 190913-SpringBoot系列教程web篇之返回文本、网页、图片的操作姿势
- 190905-SpringBoot系列教程web篇之中文乱码问题解决
- 190831-SpringBoot系列教程web篇之如何自定义参数解析器
- 190828-SpringBoot系列教程web篇之Post请求参数解析姿势汇总
- 190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总
- 190822-SpringBoot系列教程web篇之Beetl环境搭建
- 190820-SpringBoot系列教程web篇之Thymeleaf环境搭建
- 190816-SpringBoot系列教程web篇之Freemaker环境搭建
- 190421-SpringBoot高级篇WEB之websocket的使用说明
- 190327-Spring-RestTemplate之urlencode参数解析异常全程分析
- 190317-Spring MVC之基于java config无xml配置的web应用构建
- 190316-Spring MVC之基于xml配置的web应用构建
- 190213-SpringBoot文件上传异常之提示The temporary upload location xxx is not valid
项目源码
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 项目:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/201-web
1. 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰Blog个人博客 https://blog.hhui.top
- 一灰灰Blog-Spring专题博客 http://spring.hhui.top
Be the first person to leave a comment!