前言

SpringBoot系列文章的《第七章:过滤器、监听器、拦截器》中,小技巧中指出,可使用@Order设置过滤器的执行顺序。由于没有自己求证过,看了相关材料后,想当然的写进了文章中,这个进行更正下。

通过过滤器名称和设置@Order的方法都是不行的。抱歉了,各位。之后在编写文章时,会本着负责且持着大胆猜测小心求证的态度,会对相关事项进行核对的!再次,抱歉,误导了大家

这里要感谢简书网友:形而上学本尊,指出此错误!再次感谢!

正确设置排序方式

《第七章:过滤器、监听器、拦截器》也有指出,利用FilterRegistrationBean可以设置排序顺序。那是否还有其他方式呢。有的,只是这种方案不是很优雅。这里简单说明下。

先说结论:可以通过过滤器的类名进行约定排序。

浅谈ServletComponentScan注解的启动方式

既然遇到了,那就简单分析下使用@WebFilter@ServletComponentScan的启动方式吧。

首先我们来看下,注解@ServletComponentScan(删除了相关注解):

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan { @AliasFor("basePackages")
String[] value() default {}; @AliasFor("value")
String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }

简单来说,此注解就是指定扫描路径的,通过valuebasePackages或者basePackageClasses。主要还是看下ServletComponentScanRegistrar类,这才是关键。

class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    private static final String BEAN_NAME = "servletComponentRegisteringPostProcessor";

    @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取包路径
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 若已注册,则更新,否则新增
if (registry.containsBeanDefinition(BEAN_NAME)) {
updatePostProcessor(registry, packagesToScan);
} else {
addPostProcessor(registry, packagesToScan);
}
} private void updatePostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) {
BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME);
ValueHolder constructorArguments = definition.getConstructorArgumentValues().getGenericArgumentValue(Set.class);
@SuppressWarnings("unchecked")
Set<String> mergedPackages = (Set<String>) constructorArguments.getValue();
mergedPackages.addAll(packagesToScan);
constructorArguments.setValue(mergedPackages);
} private void addPostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
// 设置类
beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);
// 设置构造函数参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
} private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
// 获取注解ServletComponentScan的属性信息
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(ServletComponentScan.class.getName()));
// 获取属性basePackages和basePackageClasses
String[] basePackages = attributes.getStringArray("basePackages");
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
Set<String> packagesToScan = new LinkedHashSet<String>();
packagesToScan.addAll(Arrays.asList(basePackages));
// basePackageClasses 最后也是根据basePackageClasses来获取塔对应的包路径
for (Class<?> basePackageClass : basePackageClasses) {
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
}
// 默认不填写时,获取的是被注解类所在包路径,所以一般放在启动类上
if (packagesToScan.isEmpty()) {
packagesToScan.add(ClassUtils.getPackageName(metadata.getClassName()));
}
return packagesToScan;
}
}

可以看见,它是一个ImportBeanDefinitionRegistrar的实现类,ImportBeanDefinitionRegistrar可以动态地装载Bean。再来看看ServletComponentRegisteringPostProcessor类,此类是个BeanFactoryPostProcessor,BeanFactory的后置处理器,简单理解就是扩展点吧。启动的时候会调用postProcessBeanFactory方法。

ServletComponentRegisteringPostProcessor源码就不贴了,简单来说,它的作用就是:扫描被@WebServlet@WebFilter@WebListener的类,最后通过对应的ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean进行注册。看见这些是不是很熟悉了。

//部分代码
static {
List<ServletComponentHandler> servletComponentHandlers = new ArrayList<ServletComponentHandler>();
servletComponentHandlers.add(new WebServletHandler());
servletComponentHandlers.add(new WebFilterHandler());
servletComponentHandlers.add(new WebListenerHandler());
HANDLERS = Collections.unmodifiableList(servletComponentHandlers);
}

关键看这个方法scanPackage:

    private void scanPackage(
ClassPathScanningCandidateComponentProvider componentProvider,
String packageToScan) {
for (BeanDefinition candidate : componentProvider
.findCandidateComponents(packageToScan)) {
if (candidate instanceof ScannedGenericBeanDefinition) {
for (ServletComponentHandler handler : HANDLERS) {
handler.handle(((ScannedGenericBeanDefinition) candidate),
(BeanDefinitionRegistry) this.applicationContext);
}
}
}
}

可以看见,通过componentProvider.findCandidateComponents(packageToScan)方法获取到对应的注解类,同时判断是否为以上说的三种,最后调用其doHandle方法完成注册功能。以下是WebFilterHandlerdoHandler方法。

现在,我们看看findCandidateComponents方法怎么获取对应注解类的。

断点之后,可以看见是AnnotationConfigEmbeddedWebApplicationContext类,

继续断点进去,最后是使用PathMatchingResourcePatternResolver类进行资源获取的。

通过递归的方式,获取所有的类:

最后关键就是这个Arrays.sort(dirContents)了。所以简单来说,可以通过class类名来达到排序效果。但这种方案要限制类名,还是使用FilterRegistrationBean之类的来设置吧。

总结

写的可能有点乱也有点水,⊙﹏⊙‖∣。主要还是想纠正下原先的错误,O__O…。知其然知其所以然,还有很长的路要走。没有写里面的细节,只是大致讲解了下。有兴趣的可以自行跟踪看看。

最后

目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。

老生常谈

  • 个人QQ:499452441
  • 微信公众号:lqdevOps

个人博客:http://blog.lqdev.cn

原文地址:http://blog.lqdev.cn/2018/08/26/%E6%97%A5%E5%B8%B8%E7%A7%AF%E7%B4%AF/correct-webfilter/

关于@webFilter使用@Order无效问题的更多相关文章

  1. SpringBoot | 第七章:过滤器、监听器、拦截器

    前言 在实际开发过程中,经常会碰见一些比如系统启动初始化信息.统计在线人数.在线用户数.过滤敏高词汇.访问权限控制(URL级别)等业务需求.这些对于业务来说一般上是无关的,业务方是无需关系的,业务只需 ...

  2. springboot @WebFilter过滤器的使用

    过滤器的用法就不多说了 新建Filter的继承类:MemberFilter(放置包需要注意) @WebFilter(urlPatterns = "/*") @Order(1) pu ...

  3. [0] DDD领域驱动设计(三) 之 聚合(根)、实体、值对象

    1.      聚合根.实体.值对象的区别? 从标识的角度: 聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识,不存在这个值对象或那个值对象的说法: 从是否只读的角度 ...

  4. 【转载自netfocus博客】聚合(根)、实体、值对象精炼思考总结

    1.内容摘要 最近在看DDD领域驱动设计,看到实体(Entity),值对象 (Value Object),以及聚合根(Aggregate Root) 时.对他们的关系有些模糊,不清楚.于是去找了找资料 ...

  5. 从壹开始微服务 [ DDD ] 之六 ║聚合 与 聚合根 (下)

    前言 哈喽大家周二好,上次咱们说到了实体与值对象的简单知识,相信大家也是稍微有些了解,其实实体咱们平时用的很多了,基本可以和数据库表进行联系,只不过值对象可能不是很熟悉,值对象简单来说就是在DDD领域 ...

  6. Springboot添加filter方法

    在springboot添加filter有两种方式: (1).通过创建FilterRegistrationBean的方式(建议使用此种方式,统一管理,且通过注解的方式若不是本地调试,如果在filter中 ...

  7. NHibernate常见错误汇总

    NHibernateSample.Data.Test.QueryHQLFixture.WhereTest: NHibernate.Hql.Ast.ANTLR.QuerySyntaxException ...

  8. Spring-Boot使用嵌入式容器,那怎么配置自定义Filter呢

    Listener.Filter和Servlet是Java Web开发过程中常用的三个组件,其中Filter组件的使用频率最高,经常被用来做简单的权限处理.请求头过滤和防止XSS攻击等.如果我们使用的是 ...

  9. Java Web之路一:过滤器(Filter)

    一.过滤器(Filter)简介 过滤器是对web资源进行拦截,做一些处理后再交给下一个过滤器或Servlet处理,主要可以拦截request和response 过滤器是以一种组件的形式与web程序绑定 ...

随机推荐

  1. ES6学习之装饰器

    定义:修饰器是一个对类进行处理的函数,用来修改类的行为 <注>:装饰器只能用来修改类及类的方法 类的装饰: 静态属性:只能通过类访问,修饰函数直接在类上操作 @testable class ...

  2. angularJS中自定义指令

    学习了angularJS一周,但是大部分时间被自定义指令占用了.博主表示自学互联网好心塞的,发现问题的视觉很狭窄,这比解决问题要更难.这篇文章首先介绍了自定义,然后介绍了在使用自定义指令遇到的问题. ...

  3. 进击的菜鸟问题1(设置checkbox全选)

    问题:设置页面标签属性时,常常使用jquery.attr("","");在设置checkbox属性时,会出现操作设置checkbox属性后,无法清除,导致第二次 ...

  4. Servlet编程实例 续4

    ---------------siwuxie095 JSP+Servlet+JDBC 继续完善登录实例,将校验逻辑改为:从数据库中获取用户信息进行校验 数据库准备 在 Navicat for MySQ ...

  5. 面试题: Spring 框架 Bean的生命周期

    [Java面试五]Spring总结以及在面试中的一些问题.   1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本 ...

  6. 2、misa统计SRR结果

    参考: https://www.sogou.com/link?url=hedJjaC291NYNxVe4xgB4c3bUxXRMqZrT93cntTAgYfyBbRAdP9kIA.. https:// ...

  7. html css将图片或div置于顶层

    在做这个功能时,图片被挡住了.. 解决办法 在这个图片的css里加上z-index:数字:(数字可以为正也可以为负数) z-index:1肯定在z-index:-1的上面 用这个属性来给div分层 是 ...

  8. C#监听窗体新建/鼠标移入移出

    在新建window窗体时会激活方法,并循环所有窗体,鼠标移动在重写方法的页面中也会激活 winform直接在继承了From窗体cs中 protected override void WndProc(r ...

  9. python接口自动化(三十六)-封装与调用--流程类接口关联续集(详解)

    简介 上一篇已经给大家都介绍过了流程类接口关联,但是由于博客的登录机制改变,所以没有办法给小伙伴们实战演练一下,那么这篇就按照上一篇计划的用jenkins来给小伙伴们演示一下流程类接口的封装和调用,其 ...

  10. Codeforces - 1181B - Split a Number - 贪心

    https://codeforces.com/contest/1181/problem/B 从中间拆开然后用大数搞一波. 当时没想清楚奇偶是怎么弄,其实都可以,奇数长度字符串的中心就在len/2,偶数 ...