前言

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. qtp ie_hook

    今天要讲的内容是注册异类子控件授予强制HOOK,名字有点抽象,简单的说就是在一个QTP可识别的A类插件窗口对象中存在着B类插件的控件对象, 最常见的例子就是在应用程序中内嵌一个Browser对象子控件 ...

  2. C#动态给EXCEL列添加下拉选项

    Microsoft.Office.Interop.Excel.Application excel=new Microsoft.Office.Interop.Excel.Application(); M ...

  3. Java探索之旅(18)——多线程(2)

    1 线程协调 目的对各线程进行控制,保证各自执行的任务有条不紊且有序并行计算.尤其是在共享资源或者数据情况下. 1.1 易变volatile cache技术虽然提高了访问数据的效率,但是有可能导致主存 ...

  4. sort命令实战

    本文参考:http://www.cnblogs.com/dong008259/archive/2011/12/08/2281214.html 东方雨中漫步者 sort命令,帮助我们依据不同的数据类型进 ...

  5. 8、非root权限下安装perl以及perl模块

    转载:http://www.cnblogs.com/nkwy2012/p/6418669.html 转载自http://www.zilhua.com 在本博客中,所有的软件安装都在服务器上,且无roo ...

  6. leetcode:234. Palindrome Linked List

    这个题目非常好.http://blog.csdn.net/u012249528/article/details/47124771给出了三种解法,其中前两个是不满足条件的,不过具有参考价值: 第一种办法 ...

  7. C#使用Json.NET解析Json

    本文转载自 http://xiaosheng.me/2016/10/01/article25/ 最近在 C# 项目中需要使用到 Json 格式的数据,我简单上网搜索了一下,基本上有两种操作 Json ...

  8. cygwin主要命令

    基本操作命令: ---------------------------------------------------------------------- --------------------- ...

  9. 字符串中去除多余的空格保留一个(MS SQL Server)

    大约2年前,写过一篇<字符串中去除多余的空格保留一个(C#)>https://www.cnblogs.com/insus/p/7954151.html 今天,Insus.NET使用MS S ...

  10. C# - char类型的一些介绍

    Char C#里面的char,其实就是System.Char类型的别名,它代表一个Unicode字符(是这样吗?),占用两个字节. 例如:char c = ‘A’; char占用两个字节,也就是16位 ...