关于@webFilter使用@Order无效问题
前言
在
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 {};
}
简单来说,此注解就是指定扫描路径的,通过value
、basePackages
或者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
的类,最后通过对应的ServletRegistrationBean
、FilterRegistrationBean
及ServletListenerRegistrationBean
进行注册。看见这些是不是很熟悉了。
//部分代码
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
方法完成注册功能。以下是WebFilterHandler
的doHandler
方法。
现在,我们看看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无效问题的更多相关文章
- SpringBoot | 第七章:过滤器、监听器、拦截器
前言 在实际开发过程中,经常会碰见一些比如系统启动初始化信息.统计在线人数.在线用户数.过滤敏高词汇.访问权限控制(URL级别)等业务需求.这些对于业务来说一般上是无关的,业务方是无需关系的,业务只需 ...
- springboot @WebFilter过滤器的使用
过滤器的用法就不多说了 新建Filter的继承类:MemberFilter(放置包需要注意) @WebFilter(urlPatterns = "/*") @Order(1) pu ...
- [0] DDD领域驱动设计(三) 之 聚合(根)、实体、值对象
1. 聚合根.实体.值对象的区别? 从标识的角度: 聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识,不存在这个值对象或那个值对象的说法: 从是否只读的角度 ...
- 【转载自netfocus博客】聚合(根)、实体、值对象精炼思考总结
1.内容摘要 最近在看DDD领域驱动设计,看到实体(Entity),值对象 (Value Object),以及聚合根(Aggregate Root) 时.对他们的关系有些模糊,不清楚.于是去找了找资料 ...
- 从壹开始微服务 [ DDD ] 之六 ║聚合 与 聚合根 (下)
前言 哈喽大家周二好,上次咱们说到了实体与值对象的简单知识,相信大家也是稍微有些了解,其实实体咱们平时用的很多了,基本可以和数据库表进行联系,只不过值对象可能不是很熟悉,值对象简单来说就是在DDD领域 ...
- Springboot添加filter方法
在springboot添加filter有两种方式: (1).通过创建FilterRegistrationBean的方式(建议使用此种方式,统一管理,且通过注解的方式若不是本地调试,如果在filter中 ...
- NHibernate常见错误汇总
NHibernateSample.Data.Test.QueryHQLFixture.WhereTest: NHibernate.Hql.Ast.ANTLR.QuerySyntaxException ...
- Spring-Boot使用嵌入式容器,那怎么配置自定义Filter呢
Listener.Filter和Servlet是Java Web开发过程中常用的三个组件,其中Filter组件的使用频率最高,经常被用来做简单的权限处理.请求头过滤和防止XSS攻击等.如果我们使用的是 ...
- Java Web之路一:过滤器(Filter)
一.过滤器(Filter)简介 过滤器是对web资源进行拦截,做一些处理后再交给下一个过滤器或Servlet处理,主要可以拦截request和response 过滤器是以一种组件的形式与web程序绑定 ...
随机推荐
- @JsonProperty 注解
是Jackson注解.fastjson有可以用. 作用在字段或方法上,用来对属性的序列化/反序列化,可以用来避免遗漏属性,同时提供对属性名称重命名,比如在很多场景下Java对象的属性是按照规范的驼峰书 ...
- jQuery对象和DOM对象的互换
Dom 对象:指的是普通的 JavaScript 对象 jQuery对象:是包装 Dom 对象后产生的对象. 一:JQuery 对象和 Dom 对象 在使用 JQuery 过程中,我们一般(也是绝大多 ...
- WPF实现右键菜单
ContextMenu类就是用来做右键菜单的对象,对于任何的控件都可以进行对ContextMenu属性的操作进行设置右键菜单的功能. 下面代码就是对一个按钮添加一个WPF右键菜单的功能: < B ...
- linux日常管理-系统服务
开机不启动不必要系统服务,节省硬件资源,解决安全隐患 调整服务有两种办法 没有这个命令就安装一下.执行这个命令,出现下面界面 按空格键选择或取消,tab切换选择确定或取消 系统服务留下 crond i ...
- java处理中日文字符串的乱码问题
——杂言:前段时间在处理音频预览问题,详见关于audiojs的研究.期间,将远端的音频下载并缓存在本地过程中,涉及到java.io.*的几个操作,发生一些乱码问题. 我以前的处理是将本地的编码转换为U ...
- Spring开发包介绍
-----------------siwuxie095 核心开发包 建立 Spring 工程时,需要引入 Spring 的开发包,否则无 ...
- C++11新标准
1. 新类型 long long和unsigned long long: char16_t 和 char32_t: 新增原始字符串: 2. 统一的初始化 C++11扩大了用大括号括起的列表(初始化列表 ...
- Spring入门第四课
注入参数详解:null值和级联属性 可以使用专用的<null/>元素标签为Bean的字符串或其他对象类型的属性注入null值. 和Struts,Hiberante等框架一样,Spring支 ...
- latex 输入矩阵
%以下函数可以写矩阵 %这个是放括号的矩阵 $A=\begin{bmatrix} 1&3\\ 3&3 \end{bmatrix}$ %这个是圆括号的矩阵 $A=\begin{pmatr ...
- 14.Nginx 文件名逻辑漏洞(CVE-2013-4547)
由于博主在渗透网站时发现现在Nginx搭建的网站是越来越多 所以对Nginx的漏洞来一个全面性的复习,本次从Nginx较早的漏洞开始分析. 2013年底,nginx再次爆出漏洞(CVE-2013-45 ...