前面一篇博文介绍了在 SpringBoot 中使用 Filter 的两种使用方式,这里介绍另一种直接将 Filter 当作 Spring 的 Bean 来使用的方式,而且在这种使用方式下,Filter 的优先级能够直接经过@Order
注解来指定;最后将从源码的角度分析一下两种不一样的使用方式下,为何@Order
注解一个生效,一个不生效java
本篇博文强烈推荐与上一篇关联阅读,能够 get 到更多的知识点: 191016-SpringBoot 系列教程 web 篇之过滤器 Filter 使用指南git
本篇博文的工程执行的环境依然是SpringBoot2+
, 项目源码能够在文章最后面 getgithub
前面一篇博文,介绍了两种使用姿式,下面简单介绍一下web
WebFilter 注解spring
在 Filter 类上添加注解@WebFilter
;而后再项目中,显示声明@ServletComponentScan
,开启 Servlet 的组件扫描websocket
@WebFilter
public class SelfFilter implements Filter {
}
@ServletComponentScan
public class SelfAutoConf {
}
复制代码
FilterRegistrationBeanapp
另一种方式则是直接建立一个 Filter 的注册 Bean,内部持有 Filter 的实例;在 SpringBoot 中,初始化的是 Filter 的包装 Bean 就是这个socket
@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 当作过滤器来注册ide
并且这种使用姿式下,Filter 的优先级能够经过@Order
注解来指定;spring-boot
设计一个 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 内部来实现相关逻辑
尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛