过滤器 Filter 与 拦截器 Interceptor 的区别
引言
说起 Filter 与 Interceptor 的区别,相信很多同学第一感觉就是容易、简单!
毕竟开发中这两个组件使用频率较高,用法也较简单。然后真回答起来有答不出个所以然来,场面尴尬😅,老丢脸了!
看着简单,一答就错,下面咱们先看结论!再做详细解说!
结论
- 底层原理不同:Filter 是 基于 函数回调 实现的; Interceptor 是基于 反射机制与动态代理 实现的。
- 使用范围不同:Filter 是 Servlet规范 的接口,依赖web容器(Tomcat等),只能在web工程中使用;Interceptor 是 Spring的组件,不依赖web容器。
- 触发时机不同:请求进入顺序: Tomcat ==> Filter ==> Servlet ==> Interceptor ==> Controller。
- 拦截范围不同:Filter 对进入容器的所有请求进行拦截;Interceptor 只会对
Controller
中请求或访问static
目录下的资源请求进行拦截。 - 注入bean情况不同:Filter 中能正常注入其他bean; Interceptor 在 springcontext 之前加载,而 bean 由 Spring管理,所以注册 Interceptor 前需要先手动注入 Interceptor ;
- 控制执行顺序不同:实际开发中,使用的通常是多个 Filter 或 Interceptor 组成的 链;Filter 中 拦截的核心方法是 doFilter(), Filter 直接按顺序执行;但是在 Interceptor 中存在 前置拦截方法 preHandle() 和 后置拦截方法 postHandle(),preHandle() 是顺序执行的,而 postHandle() 是反顺序执行的。
原理
函数回调
函数回调,简称回调(callback),是指通过函数参数传递到其它代码的,某一块可执行代码的引用。
Java中没有指针,不能将函数名作为参数传递,只能通过反射、直接调用、接口调用、Lambda表达式等方法来实现函数回调。这里用Lambda表达式给大家做个演示:
请求类:
public class Request{
public void send(CallBack callBack) {
System.out.println("[Request]:发送请求");
}
}
回调接口:
public interface CallBack {
void processResponse();
}
测试类:
public class Main {
public static void main(String[] args) {
Request request = new Request();
request.send(()-> System.out.println("[CallBack]:监听到请求,进行处理响应"));
}
}
注:想看看回调其他写法的可以看看这篇文章:Java回调的四种写法(反射、直接调用、接口调用、Lamda表达式) - 腾讯云开发者社区-腾讯云
过滤器Filter 与 拦截器 Interceptor 原理
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
}
这是一个自定义的过滤器,doFilter()方法中传入了一个接口参数FilterChain,这就是一个接口调用的函数回调。FilterChain接口中就只有一个回调方法doFilter()。
Interceptor 的原理就是一个jdk的动态代理,这里就不作演示了。
Interceptor 注入其他bean
实际开发中,通常通过实现 HandlerInterceptorAdapter 来自定义拦截器,而不是直接使用 HandlerInterceptor。
- 造成testService为null的原因就是拦截器比springcontext先加载,从下面的代码中也可以看到,拦截器是手动直接加入到注册表表中的,所以使用 @Bean 注解又手动注入了一次拦截器。此时拦截器中就可以注入其他bean了。
@Configuration
public class GlobalWebAppConfigurer implements WebMvcConfigurer {
/**
* 将拦截器添加到注册表中
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
}
// 手动注入拦截器
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
}
Interceptor 执行顺序
由spring mvc的源码决定的,在核心转发器 DispatcherServlet 的 doDispatch 中,applyPreHandle()、
applyPostHandle()对拦截器数组的调用顺序是相反的。具体源码等写到springmvc再分析。