- 联系方式:1761430646@qq.com
- 编写时间:2022年12月17日11:28:31
- 博客地址:www.zeroeden.cn
- 菜狗摸索,有误勿喷,烦请联系
1. 前言
-
只做如何使用,不做为什么会这样(菜狗水平,目前顶不住)
-
默认搭建好了
SpringBoot
环境/** * @author: Zero * @time: 2022/12/9 * @description: */ @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } }
/** * @author: Zero * @time: 2022/12/9 * @description: 控制层 */ @RestController public class TestController { @GetMapping("/test") public String test() { System.out.println("====这是控制层test()方法===="); return "success"; } @GetMapping("/testA") public String testA() { System.out.println("====这是控制层testA()方法===="); return "success"; } @GetMapping("/testA/A") public String testAA() { System.out.println("====这是控制层testA/A()方法===="); return "success"; } @GetMapping("/testB") public String testB() { System.out.println("====这是控制层testB()方法===="); return "success"; } @GetMapping("/testB/B") public String testBB() { System.out.println("====这是控制层testBB()方法===="); return "success"; } @GetMapping("/my") public String my() { System.out.println("====my()方法===="); return "success"; } @GetMapping("/my.html") public String myHtml() { System.out.println("====myHtml()方法===="); return "success"; } @GetMapping("/my.json") public String myJson() { System.out.println("====myJson()方法===="); return "success"; } }
2. Filter
2.1 SpringBoot 整合多个 Filter
-
其中一个
Filter
实现代码模板如下/** * @author: Zero * @time: 2022/11/28 * @description: */ public class FilterA1 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("--------[FilterA1]过滤器: init() -- 执行初始化方法--------"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("--------[FilterA1]过滤器: doFilter() -- 放行前 --------"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("--------[FilterA1]过滤器: doFilter() -- 放行后 --------"); } @Override public void destroy() { System.out.println("--------[FilterA1]过滤器: destroy() -- 执行销毁方法--------"); } }
-
按照上述代码模板,同时Copy多个,为其起不同类名并改动输出的
Filter
名称即可,如下
2.1.1 使用配置类
-
通过在配置类中为各
Filter
注册成Bean
并做相关过滤器配置即可 -
配置如下
/** * @author: Zero * @time: 2022/12/9 * @description: Filter 配置类 */ @Configuration public class FilterConfiguration { @Bean public FilterA1 createFilterA1(){ return new FilterA1(); } @Bean public FilterA2 createFilterA2(){ return new FilterA2(); } @Bean public FilterB1 createFilterB1(){ return new FilterB1(); } @Bean public FilterC1 createFilterC1(){ return new FilterC1(); } @Bean public FilterRegistrationBean<FilterA1> filterA1(){ FilterRegistrationBean<FilterA1> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(createFilterA1()); // 设置过滤器 filterRegistrationBean.addUrlPatterns("/*"); // 增加拦截路径 return filterRegistrationBean; } @Bean public FilterRegistrationBean<FilterA2> filterA2(){ FilterRegistrationBean<FilterA2> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(createFilterA2()); // 设置过滤器 filterRegistrationBean.addUrlPatterns("/*"); // 增加拦截路径 return filterRegistrationBean; } @Bean public FilterRegistrationBean<FilterB1> filterB1(){ FilterRegistrationBean<FilterB1> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(createFilterB1()); // 设置过滤器 filterRegistrationBean.addUrlPatterns("/*"); // 增加拦截路径 return filterRegistrationBean; } @Bean public FilterRegistrationBean<FilterC1> filterC1(){ FilterRegistrationBean<FilterC1> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(createFilterC1()); // 设置过滤器 filterRegistrationBean.addUrlPatterns("/*"); // 增加拦截路径 return filterRegistrationBean; } }
-
启动程序,访问
/test
路径结果如下
2.1.2 @Component
-
直接在每个
Filter
中加入注解@Component
,将其对应Filter
注入到容器中即可 -
启动程序,访问
/test
路径结果如下
2.1.3 @WebFilter + @ServletComponentScan
-
在每个
Filter
上加入注解WebFilter
(标明这是个过滤器),以及在启动类上加入注解@ServletComponent
,配置Filter
的扫描路径 -
启动程序,访问
/test
路径结果如下 -
注意:
-
@WebFilter
是Servlet
中的注解,如下图 -
它的作用就是去标明这个类是
Filter
-
不能说只加
@WebFilter
,而不在启动类上加入@ServletComponentScan
注解标明Servlet
组件的扫描路径,否则的话Filter
是不生效的 -
切记,切记,这两个注解得搭配使用才行
-
2.2 SpringBoot 整合多个 Filter 为其设置过滤顺序
- 注意:这是在
SpringBoot
中整合多个Filter
的基础上去设置每个Filter
的过滤顺序,也就是此章节是建立在【2.1】的整合基础下,整合多个Filter
时有多种方式,这里就有多少种设置过滤顺序的方式
2.2.1 在配置类中配置
2.2.1.1 演示
-
在章节【2.1.1】中,当我们配置多个过滤器时,可以看到在配置类总我们的代码从上到下是按照
FilterA1
,FilterA2
,FilterB1
,FilterC1
的顺序配置的 -
然后访问接口时,从控制台输出中我们也可以看到其过滤顺序是刚好也是
FilterA1
,FilterA2
,FilterB1
,FilterC1
-
所以现在可以轻易得出个小结:在使用配置类的方式去配置
Filter
时,默认情况下是按照Filter
的配置顺序去控制Filter
的过滤顺序的 -
现在我们可以在配置类中调整下配置
Filter
时的顺序做下测试,看小结是否准确,比如说把FilterA2
的配置调到最后,如下图 -
现在访问接口时,其图如下,可以看到过滤顺序其结果为
FilterA1
,FilterB1
,FilterC1
,FilterA2
,正如小结所说,证明小结是准确的(默认情况下是按照Filter
的配置方法顺序去控制Filter
的过滤顺序的) -
但是在使用配置类的方式时,就真的只能通过去调整配置
Filter
方法的顺序去控制Filter
的过滤顺序么 -
答案不是的
-
在配置每个
Filter
时,其FilterRegistrationBean
对象内部有一个属性order
,其作用是配置当前Filter
的过滤顺序,值越小,顺序排在越前,且默认值为2147483647(int
的最大值)-
注意:这个
order
属性是FilterRegistrationBean
类通过间接继承父类RegistrationBean
得来的
-
-
现在我们为每个
Filter
设置其order
值来控制器过滤顺序,配置如下 -
可以看到其
order
值从大到小为FilterA1
,FilterA2
,FilterB1
,FilterC1
,按照order
值越小,过滤排在越前的规则,也就是实际上过滤顺序该为FilterC1
,FilterB1
,FilterA2
,FIlterA1
-
访问
/test
接口,其结果如下
2.2.1.2 章节小结
- 在使用配置类这种方式去配置
Filter
时 - 是通过设置
FilterRegistrationBean
对象中order
值来控制过滤顺序的 - 其
order
值越小,过滤顺序排在越前,order
的默认值为int
的最大值,也即是2147483647 - 对于每个
Filter
,如果说设置了order
值,就按照order
值的大小来控制过滤顺序 - 如果没有,
order
就为默认值,当多个Filter
的order
值相等时或者都没有设置Order
值时,默认会按照在配置类中配置Filter
时的方法顺序从上到下来顺序排序过滤顺序
2.2.2 @Component + @Order
2.2.2.1 演示
-
在章节【2.1.2】中,我们可以通过直接在每个
Filter
上加入注解@Component
去配置过滤器 -
先讲个小结:这时候默认的过滤顺序其实是通过比较类名来决定的
-
在章节【2.1.2】中,我们可以看到这4个的类名比较顺序如下
-
启动程序,访问
/test
接口,其过滤顺序也正好是FilterA1
,FilterA2
,FilterB1
,FilterC1
-
现在在此基础上,我们再增加两个
Filter
,分别是FilterA0
,FilterB2
-
再次启动程序,访问
/test
接口,其过滤顺序也正好是FilterA0
,FilterA1
,FilterA2
,FilterB1
,FilterB2
,FilterC1
-
可见上述小结是正确的(使用
@Component
这种方式,默认情况下是通过比较类名来决定过滤顺序的) -
而对于这种方式,在每个
Filter
上再加入注解@Order
并设置其值,即可配置当前Filter
的过滤顺序 -
@Order
值正如上个章节【2.2.1】一样,是配置过滤顺序的(只不过换成了使用注解来设置的方式),过滤顺序规则还是依旧,值越小,过滤顺序越前,默认值为int
的最大值,即为2147483647 -
现在我们可以在每个
Filter
上,加入注解@Order
,并配置其值,其配置如下 -
启动程序,访问
/test
接口,其过滤顺序结果如下 -
可以看到过滤顺序如我们所愿
2.2.2.2 章节小结
- 在使用
@Component
这种方式去配置Filter
时 - 我们可以在对应
Filter
上通过加入注解@Order
设置值来设置当前过滤器过滤顺序 - 其值设置的越小,过滤顺序排在越前,默认值为
int
的最大值,也即是2147483647 - 对于每个
Filter
,会按照注解@Order
中的值来控制过滤顺序 - 如果说没有配置此注解,或者说配置的值相等,则会默认通过比较类名(越小过滤顺序越前)来决定过滤顺序
2.2.3 @WebFilter + @ServletComponentScan
-
对于这种配置方式,默认还是按照比较类名的方式来决定过滤顺序(越小过滤顺序越前)
-
但目前来说,比较遗憾的是这种配置方式没有额外的方法可以配置其过滤顺序
-
也就是说,
SpringBoot
使用@WebFilter
+ServletComponentScan
这种配置Filter
的方式,就只能通过比较Filter
类名的方式进行设置过滤顺序 -
原本想着默认还是按照比较
Filter
类名来设置过滤顺序的,那么我们在注解@WebFilter
上,去设置其filterName
的值来看能不能通过这种方式控制过滤顺序,很遗憾,不能,其实验如下 -
后面又突发奇想,想着
@WebFilter
+ServletConponentScan
,看能不能通过加上注解@Order
的方式去控制过滤顺序,然而,还是不行,其实验如下
2.3 SpringBoot 整合 Filter 设置过滤规则
-
先说个结论:就是
@Component
这种SpringBoot
整合Filter
的方式,是设置不了过滤规则的(至少目前俺是找不到有啥方式可以配置),而另外两种方式就行 -
说下另外两种方式如何配置:
-
使用配置类配置
-
在配置
Filter
的方法中,FilterRegistrationBean
类中可以调用addUrlPatterns
方法(可变长参数)去增加过滤路径,也可以调用setUrlPatterns
方法设置过滤路径 -
分别访问
/test
,/testA
,/testA/A
,/testB
,/my
接口,其实验结果如下 -
同时在增加过滤匹配规则时支持后缀匹配(也就代表过滤匹配静态资源)
-
分别访问接口
/test
,/testA
,/my.json
,my.html
接口,其实验结果如下
-
-
@WebFilter
+ServletComponentScan
-
在
@WebFilter
注解中,有一个名为urlPatterns
的字符串数组属性,即是用来设置过滤匹配规则的 -
这里就不再演示了,它也支持后缀匹配
-
-
2.4 SpringBoot 整合 Filter 其他注意事项
2.4.1 过滤规则 /* 和 /** 是不一样的
-
直接先说结论然后验证
-
在
SpringBoot
整合Filter
中,设置过滤规则时-
/*
表示匹配URL
以/
为前缀的任意请求(如果是/api/*
,就代表匹配URL
以/api
为前缀的任意请求)-
设置过滤规则为
/*
,且准备几个接口 -
分别访问接口
/test
,/testA
,/testA/A
,/my.html
,/my.json
-
可以看到所有请求都匹配上了,也就证明了
/*
是过滤所有URL
以/
为前缀的请求
-
-
在设置
Filter
的过滤规则时,是没有/**
这种写法-
按照上述例子,改过滤规则为
/**
,其他不变 -
分别访问接口
/test
,/testA
,/testA/A
,/my.html
,/my.json
-
可以看到,过滤规则
/**
根本不生效
-
-
2.4.2 使用配置类方式时在某种特殊情况下是无法在过滤器中注入Bean的
- 如标题所言,这种特殊情况就是方法中使用
FilterRegistrationBean
对象设置Filter
时,如果此Filter
是直接new
出来的话,那么此时是无法在此过滤器中注入Bean
的
-
如下演示这种配置方式情况,配置如下图
-
启动程序,访问任意接口,会直接报空指针异常
-
至于为什么
-
一开始我是觉得
new
出来的Filter
实际上是不受IOC
容器管理的(只是封装后的FilterRegistrationBean
有而已),所以注入不了其他Bean
-
后面看到别人分析,说
SpringMVC
的启动是在DispatchServlet
里面做的,而它是在Listener
和Filter
之后执行,如果我们想在Listener
和Filter
里面@Autowired
某个Bean
,肯定是不行的,因为Filter
初始化的时候,此时Bean
还没有初始化,所以无法自动装配 -
菜狗表示没看过源码,暂时无法验证
3. Interceptor
3.1 SpringBoot 整合多个 Interceptor
-
其中一个
Interceptor
实现代码模板如下/** * @author: Zero * @time: 2022/12/4 * @description: 拦截器 */ //@Component public class InterceptorA1 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("--------拦截器[InterceptorA1]: preHandle() -- 拦截前的处理--------"); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("--------拦截器[InterceptorA1]: postHandle() -- 拦截处理--------"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("--------拦截器[InterceptorA1]: afterCompletion() -- --------"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
-
按照上述代码模板,同时COPY多个,为其起不同类名并改动输出的
Interceptor
名称即可,如下 -
接下就是配置的事了
-
由于
Interceptor
是Spring
框架自带的,所以说直接通过实现了WebMvcConfigurer
接口配置类,重写addInterceptors(InterceptorRegistry registry)
方法直接配置就好 -
配置如下图所示
-
启动程序,访问接口
/test
,结果如下图所示 -
可以见到多个
Interceptor
生效,整合成功
3.2 SpringBoot 整合多个 Interceptor 为其设置拦截顺序
- 在默认情况下,
Interceptor
的拦截顺序是与在addInterceptors()
方法内部的注册顺序有关的,Interceptor
注册的越早,其拦截顺序越前
-
下面我们可以调整下在刚刚的
addInterceptors()
方法内部注册的Interceptor
顺序,如下图所示 -
启动程序,访问接口
/test
,结果如下图所示 -
可以看到,拦截顺序正如我们配置
Interceptor
的顺序一致,结论正确 -
但是正如同配置
Filter
那样,可以通过设置order
值来决定过滤顺序,Interceptor
也同样可以做 -
SpringBoot
使用InterceptorRegistration
类来统一封装Interceptor
,其内部含有属性order
用来设置拦截顺序,order
值越小,拦截顺序排在越前(默认值为0) -
现在我们可以为各个
Interceptor
设置order
值做下测试,配置如下 -
启动程序,访问接口
/test
,结果如下图所示 -
可以看到其拦截顺序正如我们配置的那样,按照
order
值从小到大排序
-
小结
- 各个
Interceptor
默认是按照order
值来决定拦截顺序的 order
值默认为0- 如果没有配置
order
值或者order
值相同的多个Interceptor
- 按照在实现了接口
WebMvcConfigurer
的配置类的addInterceptors
方法中注册Interceptor
的顺序排列
- 各个
3.3 SpringBoot 整合 Interceptor 设置拦截规则
- 正如
SpringBoot
整合Interceptor
的方式一样,设置拦截规则也是在addInterceptors()
方法内部实现,直接调用addPathPatterns()
方法(可变长参数)即可添加拦截规则
-
下面做下演示配置
-
启动程序,分别访问接口
/test
,/testA
,/my.html
,实验结果如下图 -
可以看到
/test
接口成功被拦截到 -
要特别说明的是,
Interceptor
的拦截规则是不支持后缀匹配的 -
比如我们配置拦截规则为
*.html
-
启动程序,访问接口
/my.html
,实验结果如下图 -
可以看到我们想要的拦截以
.html
为结尾的请求是失效的,也就证明了Intercetptor
的拦截规则是不支持后缀匹配的
3.4 拦截规则 /* 和 /** 是不一样的
-
直接先说结论然后验证
-
在
SpringBoot
整合Interceptor
中,设置拦截规则时-
/*
表示匹配URL
以/
开头,后面最多只能有一级的请求(比如说设置拦截规则为/api/*
,则能拦截URL为/api
,/api/test
的请求,而/api/test/hello
(后面跟了两级)是无法拦截的)-
设置拦截规则为
/testA/*
,且准备几个接口 -
启动程序,分别访问接口
/test
,/testA
,/testA/A
,/testA/A/A
,实验结果如下 -
可以看到,只有访问接口
/testA/A
时拦截器才拦截到,而/testA/A/A
无效,也就证明结论是正确的(/*
后面最多只能加一级路径,否则不生效) -
特别说明的是,请求路径
/testA
和/testA/
是不同的,前者是根路径/
后面只有一级路径,而后者根路径/
后面跟的是二级路径,只是二级路径那里为空字符而已
-
-
/**
代表匹配URL
以/
开头的任意请求(后面无路径时,/
可以省略,比如说拦截规则为/api/**
,实际上请求/api
也会拦截到)–这里就不再演示效果了
-
-
假设以访问静态资源为例,简单来说:
/*
是拦截所有的文件夹,但是不包含子文件夹–后面只有一级/**
是拦截所有的文件夹以及里面包含的子文件夹–后面可以包含多级
4. 总结
4.1 Filter
-
Filter
配置方式区别: -
使用
FIlter
注意事项 -
Filter
设置过滤规则注意事项 -
总结
- 总的来说,使用配置类的方式最好,功能最全,统一集中管理各个
Filter
,后面维护起来也方便 - 如果想要使用比较简单,又不用配置拦截路径的话,使用
@Component
+@Order
这种配置方式,如果不用设置过滤顺序的话,@Order
还能忽略掉 @WebFilter
+@ServletComponentScan
个人感觉用的比较少,感觉像是@SpringBoot
为了兼容@WebFilter
弄出来的- 所以总得来说,推荐使用配置类的方式,并且在过滤器中,离业务层接口比较远,应该做的是一些无关业务的操作,比如说字符集编码设置等等,同时,如果项目是前后端不分离的话,并且如果有这个需求的话,过滤器可以对静态资源进行过滤
- 总的来说,使用配置类的方式最好,功能最全,统一集中管理各个
4.2 Interceptor
-
Interceptor
配置拦截规则注意事项 -
总结
Interceptor
离业务层比较近,通常用来做一些有关业务的事情,比如说登录状态,权限认证等等- 而且
Interceptor
个人感觉主要是针对接口拦截,也就是不理静态资源的访问(如果项目是前后端不分离的话) - 同时,要特别注意拦截规则
/*
和/**
的区别,这是与Filter
的配置过滤规则走的是不一样的流程
5. 引用
-
[spring boot:多个filter/多个interceptor/多个aop时设置调用的先后顺序(spring boot 2.3.1)](https://www.cnblogs.com/architectforest/p/13344376.html)
# SpringBoot # 过滤器 # Filter # Interceptor # 拦截器 # 区别 # 联系 # 拦截规则