- 联系方式: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 # 拦截器 # 区别 # 联系 # 拦截规则