前言

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. Maven jenkins +Jmeter自动化测试

    Maven jenkins +Jmeter自动化测试 1. Jenkins中集成jmeter-maven插件 http://my.oschina.net/u/1377774/blog/168969 2 ...

  2. 将gridview 的数据导出EXCEL

    gridview数据 单击“导出EXCEL”按钮后        1.在上面的代码中,先将gridview绑定到指定的数据源中,然后在button按钮(用来做导出到EXCEL的)的事件中,写入相关的代 ...

  3. spring 4.0 注解数据验证2

    在spring 4.0 注解数据验证1中有基本的数据验证方法.还是那个POJO: package com.suyin.pojo; import java.lang.reflect.Field; imp ...

  4. 项目一:第五天 1、区域数据(pinyin4j-简码,城市编码) 2、Web层代码重构(model对象,分页代码提取) 3、区域分页查询 3、分区添加功能 4、定区管理管理-添加,分页

    Service: /** * @Description: 1.保存定区  2.让分区关联定区 * 对象三种状态 1.持久态(被session管理对象-一级缓存中有对象) 2.托管态(有OID标识,数据 ...

  5. 杭电acm 1040题

    本题是一个非常简单的升序排序题目,但那时在做的时候把题目看错了,导致花费了大量的时间来检查为什么WA,最后发现题目看错了..... /********************************* ...

  6. R: data.frame 生成、操作数组。重命名、增、删、改

    ################################################### 问题:生成.操作数据框   18.4.27 怎么生成数据框 data.frame.,,及其相关操 ...

  7. datatables的使用

    在开发web项目中,界面就是一个以丰富友好的样式来展现数据的窗口,同样的数据不用的展现形式给人不同的体验,数据列表是数据是一种常见展现形式,对于数据列表的一个最基本的要求就是能够实现分页以及检索功能. ...

  8. redis系列:通过文章点赞排名案例学习sortedset命令

    前言 这一篇文章将讲述Redis中的sortedset类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了. 项目Github地址:https://github.com/rainbowda/ ...

  9. java实例练习——基于TCP/IP协议的多客户端通信

    先说一下大概的思路: 应用多线程来实现服务器与多客户端之间的通信 1.服务器端创建ServerSocket,循环调用accept()等待客户端连接: 2.客户端创建一个Socket并请求与服务器端连接 ...

  10. bzoj4435: [Cerc2015]Juice Junctions(最小割树+hash)

    传送门 首先最大流等于最小割,那么可以转化为最小割树来做(不知道什么是最小割树的可以看看这题->这里) 具体的做法似乎是$hash[i][j]$表示最小割为$i$时点$j$是否与$S$连通 然后 ...