目录
一、原因
二、自定义过滤器
1、web.xml配置方式
2、SpringBoot项目配置方式
方式1
方式2
方式3
方式4
三、源码解析
1、调用起点--StandardWrapperValve类
2、执行者--ApplicationFilterChain类
一、原因
虽然也工作了这么久,但是对过滤器具体实现原理,到底是怎么调用的,谁调用的,一直都是模棱两可。因此专门研究了一下,也记录下来给还不太了解的你做一个参考。
首先要知道的是Filter是Servlet规范的一部分,是Servlet容器(如Tomcat)实现的,并不是spring实现的。Filter接口是在tomcat的jar包中
二、自定义过滤器
我们先自定义一个自己的过滤器。
1、web.xml配置方式
2、SpringBoot项目配置方式
方式1
可以使用@WebFilter+@ServletComponentScan的方式
方式2
创建一个类去实现 ServletContextInitializer 接口,并把它注册为一个 Bean,Spring Boot 会负责调用这个接口的 onStartup 方法。
@Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
FilterRegistration.Dynamic registration = servletContext.addFilter("filter", filter);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/");
};
}
方式3
可以使用FilterRegistrationBean 进行API级别的注册,注意,在这种情况下可以对Filter order进行设置,而使用spring的@Order注解是无效的
@Bean
public ServletRegistrationBean asyncServletServletRegistrationBean(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new AsyncServlet(),"/");
registrationBean.setName("MyAsyncServlet");
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
FilterRegistrationBean其实也是通过 ServletContextInitializer 来实现的,它实现了 ServletContextInitializer 接口
方式4
直接实现Filter接口,并使用@Component注解标注为组件自动注入bean。
package com.demo.aop;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
@Component
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
System.out.println("DemoFilter:" + request.getRequestURL().toString());
//执行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
特点:
可以拿到原始的http请求和响应的信息,但是拿不到真正处理这个请求的方法的信息
存在的问题:
通过Filter只能拿到http的请求和响应,只能从请求和响应中获得一些参数。当前发过来的这个请求实际上真正是由哪个控制器的哪个方法来处理的,在Filter里面是不知道的,因为javax.servlet.Filter是J2EE规范中定义的,J2EE规范里面实际上并不知道与spring相关的任何内容。而我们的controller实际上是spring mvc定义的一套机制。如果你需要这些信息,那么就需要使用拦截器Interceptor
三、源码解析
1、调用起点--StandardWrapperValve类
过滤器实现起点在ApplicationFilterChain类的doFilter()方法。StandardWrapperValve#invoke()方法调用ApplicationFilterChain#doFilter()方法,从此开启了filter过滤器链的调用。
2、执行者--ApplicationFilterChain类
在ApplicationFilterChain#doFilter()方法中调用internalDoFilter(request, response)
在internalDoFilter方法中,this.pos++通过自增实现自己调用自己后,可以获取过滤器链中下一个过滤器继续执行。
filter.doFilter(request, response, this)在调用过滤器的doFilter()方法,并将自己作为参数传入,然后在我们自定义的过滤器中执行filterChain.doFilter(servletRequest, servletResponse),从而实现自己调用自己。
debug示例
过滤器链调用示意图
过滤器链执行完后,会调用servlet.service(request, response)方法,这个servlet就是我们最熟悉的SpringMVC的DispatchServlet()
至此就实现了,浏览器发送http请求到达tomcat,tomcat调用filter过滤器链,过滤器链执行完后再调用SpringMVC中DispatchServlet类的service方法,从而实现对Controller的调用,最后再层层返回给浏览器,完成整个调用过程。