过滤器
什么是过滤器
- 官方理解:依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等
- 自身理解:对所有请求进行过滤操作,获取请求携带的数据或者修改请求的某些参数。filter本质上是一种特殊的servlet,它是一种起过滤作用的特殊servlet.
Springboot 配置过滤器
-
自定义过滤器
过滤器类实现 javax.servlet.Filter。然后添加注解 @WebFilter(filterName=“过滤器名称” urlPatterns=“过滤规则” )
@Order(int) 注解,配合 @WebFilter 注解使用,用于多个过滤器时定义执行顺序,值越小越先执行。(大多数文章都这么说,其实是有问题的)
-
添加 @ServletComponentScan 注解
在启动类上加一个注解 @ServletComponentScan(basePackages=“需要扫描的过滤器”)
简单过滤器-上代码:
1.实现Filter接口并重写方法
/**
* @WebFilter时Servlet3.0新增的注解,原先实现过滤器,需要在web.xml中进行配置,而现在通过此注解,启动启动时会自动扫描自动注册。
*
* @WebFilter filterName 定义注册的过滤器的名字
* urlPatterns 定义要拦截所有的请求
*
*/
@Order(1)
@WebFilter(filterName="userFilter1",urlPatterns={"/"})
public class UserFilter1 implements Filter {
public UserFilter1(){
System.out.println("Bean的加载顺序-1");
}
Logger logger = LoggerFactory.getLogger(UserFilter1.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("过滤器初始化1");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("请求处理1");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("销毁1");
}
}
2.添加注解扫描
@SpringBootApplication
@ServletComponentScan
public class FilterServiceApplication {
public static void main(String[] args) {
SpringApplication.run(FilterServiceApplication.class, args);
}
}
3.拦截器结果
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )___ | '_ | '_| | '_ / _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |___, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.3)
2022-06-08 11:01:16.678 INFO 10576 --- [ main] c.e.f.FilterServiceApplication : Starting FilterServiceApplication using Java 1.8.0_202 on LAPTOP-7U17USMK with PID 10576 (E:\allproject\log-service-select\FilterService\target\classes started by Lenovo in E:\allproject\log-service-select)
2022-06-08 11:01:16.680 INFO 10576 --- [ main] c.e.f.FilterServiceApplication : No active profile set, falling back to default profiles: default
2022-06-08 11:01:17.279 INFO 10576 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8888 (http)
2022-06-08 11:01:17.284 INFO 10576 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-06-08 11:01:17.285 INFO 10576 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.56]
2022-06-08 11:01:17.425 INFO 10576 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-06-08 11:01:17.425 INFO 10576 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 711 ms
Bean的加载顺序-1
2022-06-08 11:01:17.461 INFO 10576 --- [ main] c.e.f.controller.filter.UserFilter1 : 过滤器初始化1
2022-06-08 11:01:17.631 INFO 10576 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path ''
2022-06-08 11:01:17.631 INFO 10576 --- [ main] c.e.f.FilterServiceApplication : Started FilterServiceApplication in 1.246 seconds (JVM running for 2.136)
2022-06-08 11:03:30.576 INFO 10576 --- [nio-8888-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-06-08 11:03:30.576 INFO 10576 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-06-08 11:03:30.577 INFO 10576 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2022-06-08 11:03:30.581 INFO 10576 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1
重点-SpringBoot多过滤器顺序问题
1 发现问题
1.先再添加两个拦截器
@Order(2)
@WebFilter(filterName="userFilter2",urlPatterns={"/"})
public class UserFilter2 implements Filter {
public UserFilter2(){
System.out.println("Bean的加载顺序-2");
}
Logger logger = LoggerFactory.getLogger(UserFilter2.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("过滤器初始化2");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("请求处理2");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("销毁2");
}
}
@Order(3)
@WebFilter(filterName="userFilter3",urlPatterns={"/"})
public class UserFilter3 implements Filter {
public UserFilter3(){
System.out.println("Bean的加载顺序-3");
}
Logger logger = LoggerFactory.getLogger(UserFilter3.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("过滤器初始化3");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("请求处理3");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("销毁3");
}
}
3.请求跟目录,观察拦截器
Bean的加载顺序-1
Bean的加载顺序-2
Bean的加载顺序-3
2022-06-08 11:07:09.037 INFO 19684 --- [ main] c.e.f.controller.filter.UserFilter3 : 过滤器初始化3
2022-06-08 11:07:09.037 INFO 19684 --- [ main] c.e.f.controller.filter.UserFilter1 : 过滤器初始化1
2022-06-08 11:07:09.037 INFO 19684 --- [ main] c.e.f.controller.filter.UserFilter2 : 过滤器初始化2
2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1
2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2
2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3
2022-06-08 11:08:31.555 INFO 19684 --- [ionShutdownHook] c.e.f.controller.filter.UserFilter3 : 销毁3
2022-06-08 11:08:31.555 INFO 19684 --- [ionShutdownHook] c.e.f.controller.filter.UserFilter1 : 销毁1
2022-06-08 11:08:31.555 INFO 19684 --- [ionShutdownHook] c.e.f.controller.filter.UserFilter2 : 销毁2
Process finished with exit code 130
问题来了
1.为什么在过滤器初始化的时候会是3 1 2的顺序
2.@order注解真的生效了嘛
2. 寻找问题1过滤器的初始化顺序原因
思考,经过多次确定过滤器的初始化并不是随机的,而是固定执行3 1 2的,那么这是为什么初步怀疑是bean的加载顺序问题,但是通过加了无参构造证明bean的加载顺序是1 2 3,因此再了解一下bean的加载顺序之后再排查原因
-
了解单一Bean的执行过程 (参考www.cnblogs.com/Anidot/arti…)
装载
- 实例化;
- 设置属性值;
- 如果实现了BeanNameAware接口,调用setBeanName设置Bean的ID或者Name;
- 如果实现BeanFactoryAware接口,调用setBeanFactory 设置BeanFactory;
- 如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext
- 调用BeanPostProcessor的预先初始化方法;
- 调用InitializingBean的afterPropertiesSet()方法;
- 调用定制init-method方法;
- 调用BeanPostProcessor的后初始化方法;
spring容器关闭
- 调用DisposableBean的destroy();
- 调用定制的destroy-method方法;
-
多个Bean的先后顺序
- 优先加载BeanPostProcessor的实现Bean
- 按Bean文件和Bean的定义顺序按bean的装载顺序(即使加载多个spring文件时存在id覆盖)
- “设置属性值”(第2步)时,遇到ref,则在“实例化”(第1步)之后先加载ref的id对应的bean
- AbstractFactoryBean的子类,在第6步之后,会调用createInstance方法,之后会调用getObjectType方法
- BeanFactoryUtils类也会改变Bean的加载顺序
总结成四点规则
Rule1:
首先读取WEB-INF/web.xml文件,该文件内一般会配置spring-config和spring-mvc。按顺序加载对应的xml文件。
Rule2:
若web.xml中还有除springmvc和默认的servlet之外的servlet(如servlet-test),那么这些servlet会按照定义的顺序执行,但一定是在默认servlet之后,springmvc之前执行,并且,若这些servlet都会分别对应一个ApplicationContext,当然也意味着分别拥有一个beanFactory。这些ApplicationContext(包括springmvc的那个),他们的parent ApplicationContext均是默认servlet对应的那个ApplicationContext(Root ApplicationContext)。
因此,若springmvc里的某个Controller尝试通过auto wire注解来注入servlet-test里面的service,那么在运行时会抛出”Could not autowire field …”异常,因为spring从springmvc那个servlet中的beanFactory(包括其父beanFactory)中找不到对应的bean。
Rule3:
在加载某个包含bean的xml文件时,按照bean的类型1)BeanFactoryPostProcessor类的bean;2)BeanPostProcessor类的bean;3)普通bean,包括import进来的(bean标签和scan标签指定的);的顺序进行加载。同类型的bean按照定义顺序加载。所有bean默认是单例的。
因此,对于BeanFactoryPostProcessor和BeanPostProcessor类型的bean,即使被放置在最后面,也会先加载哦。这种设计还是挺人性化的^_^
Rule4:
component-scan生成的bean的默认id是类名(首字母小写),例如testService1。
<bean>标签生成的bean的默认id是: 包名.类名#数字,例如qk.spring.beanFactory.service1.TestService1#0
如果component-scan和bean标签生成的bean有冲突(即bean的id相同),并且都是单例(默认是单例),那么不会重复创建,只保留最先创建出来的那个。
最后总结一下bean加载的顺序
- 首先BeanPostProcessor已经各种内建bean优先加载
- 被依赖的bean优先加载
- 其余的按照扫描包的顺序加载
- 同一个包中,由于返回的是一个Set,所以顺序和类名相关
3.过滤器初始化分析
-
springmvc 中如果有多个过滤器,那么过滤器的初始化顺序是web.xml中配置由下到上的
根据排查并非过滤器初始化顺序不对并非Bean的加载顺序问题,具体原因暂未找到,如果有大佬知道麻烦指点一二,不胜感激,后续如果找到原因,会更新
3. @Order是否能控制顺序
- 先看官方文档解释
{@code @Order} defines the sort order for an annotated component. Since Spring 4.0, annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their {@code @Bean} method). While such order values may influence priorities at injection points, please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).
翻译:
{@code @Order}定义了一个带注释组件的排序顺序。从Spring 4.0开始,Spring支持许多种类的组件基于注释的排序,甚至包括考虑到目标组件的顺序值的集合注入(来自它们的目标类或来自它们的{@code @Bean}方法)。虽然这样的顺序值可能会影响注入点的优先级,但请注意,它们不会影响单例启动顺序,单例启动顺序是由依赖关系和{@code @DependsOn}声明确定的正交关系(影响运行时确定的依赖图)。
最开始@Order注解用于切面的优先级指定;在 4.0 之后对它的功能进行了增强,支持集合的注入时,指定集合中 bean 的顺序,并且特别指出了,它对于但实例的 bean 之间的顺序,没有任何影响。
目前用的比较多的有以下3点:
- 控制AOP的类的加载顺序,也就是被
@Aspect标注的类 - 控制
ApplicationListener实现类的加载顺序 - 控制
CommandLineRunner实现类的加载顺序
- 开始试验order是否可以改变拦截顺序
-
改变@Order 的参数,代码比较简单省略。 将原来 UserFilter1-1 、 UserFilter2-2 、UserFilter3-3 改成 UserFilter1-2 、 UserFilter2-3、UserFilter3-1那么拦截顺序预期将会变成
-
2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3 变成=================》 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2实际结果:
2022-06-08 19:51:56.354 INFO 21384 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1 2022-06-08 19:51:56.354 INFO 21384 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2 2022-06-08 19:51:56.354 INFO 21384 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3 与预期值不同发现没有变化?提出疑问,Order是否能改变所有Bean的顺序,是否能改变拦截器的顺序?
查询资料得:
不是所有的Bean都可以通过@Order这个标注进行顺序的控制。你把@Order这个标注加在普通的方法上或者类上啥用没有.这种方式过滤器的顺序是通过类名来实现自定义Filter顺序的改变类名:将UserFilter1 => C_UserFilter1,UserFilter2 => A_UserFilter2,UserFilter3 => B_UserFilter1
@Order(1) @WebFilter(filterName="userFilter3",urlPatterns={"/"}) public class B_UserFilter3 implements Filter { public B_UserFilter3(){ System.out.println("Bean的加载顺序-3"); } Logger logger = LoggerFactory.getLogger(B_UserFilter3.class); @Override public void init(FilterConfig filterConfig) throws ServletException { //logger.info("过滤器初始化3"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("请求处理3"); HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; filterChain.doFilter(request, response); } @Override public void destroy() { logger.info("销毁3"); } } @Order(2) @WebFilter(filterName="userFilter1",urlPatterns={"/"}) public class C_UserFilter1 implements Filter { public C_UserFilter1(){ System.out.println("Bean的加载顺序-1"); } Logger logger = LoggerFactory.getLogger(C_UserFilter1.class); @Override public void init(FilterConfig filterConfig) throws ServletException { // logger.info("过滤器初始化1"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("请求处理1"); HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; filterChain.doFilter(request, response); } @Override public void destroy() { logger.info("销毁1"); } @Order(3) @WebFilter(filterName="userFilter2",urlPatterns={"/"}) public class A_UserFilter2 implements Filter { public UserFilter2(){ System.out.println("Bean的加载顺序-2"); } Logger logger = LoggerFactory.getLogger(UserFilter2.class); @Override public void init(FilterConfig filterConfig) throws ServletException { //logger.info("过滤器初始化2"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("请求处理2"); HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; filterChain.doFilter(request, response); } @Override public void destroy() { logger.info("销毁2"); } }预期为:
Bean的加载顺序-1 Bean的加载顺序-2 Bean的加载顺序-3 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2 2022-06-08 11:08:03.954 INFO 19684 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3 变成=================》 Bean的加载顺序-2 Bean的加载顺序-3 Bean的加载顺序-1 2022-06-08 20:06:51.543 INFO 9872 --- [nio-8888-exec-1] c.e.f.controller.filter.A_UserFilter2 : 请求处理2 2022-06-08 20:06:51.543 INFO 9872 --- [nio-8888-exec-1] c.e.f.controller.filter.B_UserFilter3 : 请求处理3 2022-06-08 20:06:51.543 INFO 9872 --- [nio-8888-exec-1] c.e.f.controller.filter.C_UserFilter1 : 请求处理1符合预期,证明这种方式@WebFilter+@ServletComponentScan 无法指定Order改变拦截顺序
那么问题来了,还有没有其他方式创建拦截器可以设置Order呢,根据查询Stack Overflow上前辈们的分析,还有其他几种方式
方式一:@WebFilter+@ServletComponentScan
如上:不演示了
方式二:@Component+@Order()
@Order(2)
@Component
public class UserFilter1 implements Filter {
public UserFilter1(){
System.out.println("Bean的加载顺序-1");
}
Logger logger = LoggerFactory.getLogger(UserFilter1.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// logger.info("过滤器初始化1");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("请求处理1");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("销毁1");
}
}
@Order(3)
@Component
public class UserFilter2 implements Filter {
public UserFilter2(){
System.out.println("Bean的加载顺序-2");
}
Logger logger = LoggerFactory.getLogger(UserFilter2.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//logger.info("过滤器初始化2");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("请求处理2");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("销毁2");
}
}
@Order(1)
@Component
public class UserFilter3 implements Filter {
public UserFilter3(){
System.out.println("Bean的加载顺序-3");
}
Logger logger = LoggerFactory.getLogger(UserFilter3.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//logger.info("过滤器初始化3");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("请求处理3");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("销毁3");
}
}
结果:Order能控制顺序,但是不能指定拦截路径,这不是裂开。。。
Bean的加载顺序-1
Bean的加载顺序-2
Bean的加载顺序-3
2022-06-08 20:16:35.668 INFO 15364 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3
2022-06-08 20:16:35.668 INFO 15364 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1
2022-06-08 20:16:35.668 INFO 15364 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2
方式三:新增个配置类,使用包装Bean注入( FilterRegistrationBean)
新增配置类:
@Configuration
public class FilterConfig{
@Bean
public FilterRegistrationBean Filter01(){
FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
filterRegistrationBean.setFilter(new UserFilter1());//设置过滤器名称
filterRegistrationBean.addUrlPatterns("/*");//配置过滤规则
filterRegistrationBean.setOrder(10); //order的数值越小 则优先级越高
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean Filter02(){
FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
filterRegistrationBean.setFilter(new UserFilter2());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setOrder(2);
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean Filter03(){
FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
filterRegistrationBean.setFilter(new UserFilter3());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setOrder(-1);
return filterRegistrationBean;
}
}
实际输出:符合实际预期
Bean的加载顺序-1
Bean的加载顺序-2
Bean的加载顺序-3
2022-06-08 20:21:43.543 INFO 1372 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter3 : 请求处理3
2022-06-08 20:21:43.543 INFO 1372 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter2 : 请求处理2
2022-06-08 20:21:43.543 INFO 1372 --- [nio-8888-exec-1] c.e.f.controller.filter.UserFilter1 : 请求处理1
既可以设置过滤的路径,又可以设置执行顺序
方式四 继承OncePerRequestFilter
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
@WebFilter(filterName = "ContentCachingFilter", urlPatterns = "/*")
public class ContentCachingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
System.out.println("IN ContentCachingFilter ");
CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(httpServletRequest);
filterChain.doFilter(cachedBodyHttpServletRequest, httpServletResponse);
}
}
package com.example.filterservice.controller.filter.fliter2;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(value = Ordered.LOWEST_PRECEDENCE)
@Component
@WebFilter(filterName = "ContentCachingFilter2", urlPatterns = "/*")
public class ContentCachingFilter2 extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
System.out.println("IN ContentCachingFilter2 ");
CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(httpServletRequest);
filterChain.doFilter(cachedBodyHttpServletRequest, httpServletResponse);
}
}
package com.example.filterservice.controller.filter.fliter2;
import org.springframework.util.StreamUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.InputStream;
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
private byte[] cachedBody;
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
}
}
实际结果
IN ContentCachingFilter2
IN ContentCachingFilter
总结,几种方式的执行顺序控制和拦截请求情况:
| 方式 | 执行顺序 | 拦截请求 |
|---|---|---|
| @Component+@Order() | ✔️ | ❌ |
| @WebFilter+@ServletComponentScan | ❌ | ✔️ |
| FilterRegistrationBean | ✔️ | ✔️ |
| 继承OncePerRequestFilter | ✔️ | ✔️ |
最后再顺带记录一下:
-
Filter执行顺序
Filter的执行顺序是按照FilterChain来执行的,FilterChain中Filter的顺序不同配置方式下的组织情况不同,具体如下:
- 基于注解配置:按照类名的字符串比较规则比较,值小的先执行
- 使用web.xml配置:根据对应的Mapping的顺序组织,谁定义在上边谁就在前