您好,登录后才能下订单哦!
这篇文章给大家介绍SpringBoot web中过滤器Filter的使用方法,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
这里介绍另外一种直接将 Filter 当做 Spring 的 Bean 来使用的方式,并且在这种使用方式下,Filter 的优先级可以直接通过@Order
注解来指定;最后将从源码的角度分析一下两种不同的使用方式下,为什么@Order
注解一个生效,一个不生效
本篇博文的工程执行的环境依然是SpringBoot2+
, 项目源码可以在文章最后面 get
前面一篇博文,介绍了两种使用姿势,下面简单介绍一下
WebFilter 注解
在 Filter 类上添加注解@WebFilter
;然后再项目中,显示声明@ServletComponentScan
,开启 Servlet 的组件扫描
@WebFilter public class SelfFilter implements Filter { } @ServletComponentScan public class SelfAutoConf { }
FilterRegistrationBean
另外一种方式则是直接创建一个 Filter 的注册 Bean,内部持有 Filter 的实例;在 SpringBoot 中,初始化的是 Filter 的包装 Bean 就是这个
@Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("orderFilter"); filter.setFilter(new SelfFilter()); filter.setOrder(-1); return filter; }
本篇将介绍另外一种方式,直接将 Filter 当做普通的 Bean 对象来使用,也就是说,我们直接在 Filter 类上添加注解@Component
即可,然后 Spring 会将实现 Filter 接口的 Bean 当做过滤器来注册
而且这种使用姿势下,Filter 的优先级可以通过@Order
注解来指定;
设计一个 case,定义两个 Filter(ReqFilter
和OrderFilter
), 当不指定优先级时,根据名字来,OrderFilter 优先级会更高;我们主动设置下,希望ReqFilter
优先级更高
@Order(1) @Component public class ReqFilter 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("req filter"); chain.doFilter(request, response); } @Override public void destroy() { } } @Order(10) @Component public class OrderFilter 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("order filter!"); chain.doFilter(request, response); } @Override public void destroy() { } }
上面两个 Filter 直接当做了 Bean 来写入,我们写一个简单的 rest 服务来测试一下
@RestController public class IndexRest { @GetMapping(path = {"/", "index"}) public String hello(String name) { return "hello " + name; } } @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
请求之后输出结果如下, ReqFilter 优先执行了
当我们直接将 Filter 当做 Spring Bean 来使用时,@Order
注解来指定 Filter 的优先级没有问题;但是前面一篇博文中演示的@WebFilter
注解的方式,则并不会生效
这两种方式的区别是什么?
@Order
注解到底有什么用,该怎么用
首先我们分析一下将 Filter 当做 Spring bean 的使用方式,我们的目标放在 Filter 的注册逻辑上
第一步将目标放在: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
下面的逻辑中包括了 ServeltContext 的初始化,而我们的 Filter 则可以看成是属于 Servlet 的 Bean
private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes( beanFactory); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, getServletContext()); existingScopes.restore(); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, getServletContext()); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
注意上面代码中的 for 循环,在执行getServletContextInitializerBeans()
的时候,Filter 就已经注册完毕,所以我们需要再深入进去
将目标集中在org.springframework.boot.web.servlet.ServletContextInitializerBeans#ServletContextInitializerBeans
public ServletContextInitializerBeans(ListableBeanFactory beanFactory) { this.initializers = new LinkedMultiValueMap<>(); addServletContextInitializerBeans(beanFactory); addAdaptableBeans(beanFactory); List<ServletContextInitializer> sortedInitializers = this.initializers.values() .stream() .flatMap((value) -> value.stream() .sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this.sortedList = Collections.unmodifiableList(sortedInitializers); }
上面有两行代码比较突出,下面单独捞出来了,需要我们重点关注
addServletContextInitializerBeans(beanFactory); addAdaptableBeans(beanFactory);
通过断点进来,发现第一个方法只是注册了dispatcherServletRegistration
;接下来重点看第二个
@SuppressWarnings("unchecked") private void addAdaptableBeans(ListableBeanFactory beanFactory) { MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory); addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig)); addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter()); for (Class<?> listenerType : ServletListenerRegistrationBean .getSupportedTypes()) { addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType, new ServletListenerRegistrationBeanAdapter()); } }
从上面调用的方法命名就可以看出,我们的 Filter 注册就在addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
上面的截图就比较核心了,在创建FilterRegistrationBean
的时候,根据 Filter 的顺序来指定最终的优先级
然后再回到构造方法中,根据 order 进行排序, 最终确定 Filter 的优先级
接下来我们看一下 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 的实例过程
private <T> List<Entry<String, T>> getOrderedBeansOfType( ListableBeanFactory beanFactory, Class<T> type, Set<?> excludes) { Comparator<Entry<String, T>> comparator = (o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()); String[] names = beanFactory.getBeanNamesForType(type, true, false); Map<String, T> map = new LinkedHashMap<>(); for (String name : names) { if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) { T bean = beanFactory.getBean(name, type); if (!excludes.contains(bean)) { map.put(name, bean); } } } List<Entry<String, T>> beans = new ArrayList<>(); beans.addAll(map.entrySet()); beans.sort(comparator); return beans; }
注意我们的 Filter 实例在T bean = beanFactory.getBean(name, type);
通过这种方式获取的 Filter 实例,并不会将 ReqFilter 类上的 Order 注解的值,来更新FilterRegistrationBean
的 order 属性,所以这个注解不会生效
最后我们再看一下,通过 WebFilter 的方式,容器类不会存在ReqFilter.class
类型的 Bean, 这个与前面的方式不同
主要介绍了另外一种 Filter 的使用姿势,将 Filter 当做普通的 Spring Bean 对象进行注册,这种场景下,可以直接使用@Order
注解来指定 Filter 的优先级
但是,这种方式下,我们的 Filter 的很多基本属性不太好设置,一个方案是参考 SpringBoot 提供的一些 Fitler 的写法,在 Filter 内部来实现相关逻辑
关于SpringBoot web中过滤器Filter的使用方法就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。