品牌型号:联想ThinkPad X1
系统:Windows10家庭版
软件版本:Spring 5.3.7
刚接触Spring那会儿,很多人分不清过滤器和拦截器,觉得都差不多反正都能拦截请求,用哪个都一样,一直到后来踩了不少坑。有次项目里要做登录校验,直接用了拦截器,结果某些静态资源的请求怎么都拦不住,排查后才发现是执行时机的问题。类似的情况其实很常见,根源就在于没理解过滤器是怎么工作的以及它和拦截器在请求处理链里各自站在哪个位置。下面就给大家介绍一下Spring过滤器实现原理,Spring过滤器和拦截器的执行顺序的相关内容。
一、Spring过滤器实现原理
过滤器并不是Spring发明的,它来自Servlet规范,是javax.servlet.Filter接口定义的东西,Spring只是做了整合。

Filter接口有三个方法init、doFilter、destroy,核心是doFilter,每次请求都走这里。方法里有个FilterChain参数,调用chain.doFilter就是把请求往后传,不调就直接终止请求流转。

过滤器跑在Servlet容器层面,也就是Tomcat这一层,比DispatcherServlet靠前,请求进来后先经过过滤器链,再到Servlet,最后才到Spring做路由分发,这个顺序是规范定死的,Spring在这里做的最关键的事是引入了DelegatingFilterProxy。
Servlet容器和Spring容器本来是两套独立的体系,DelegatingFilterProxy注册在Servlet容器里,但实际逻辑委托给Spring里的Bean执行,这样过滤器就能用上依赖注入这些能力了。像Spring Security就是走这条路子,整个安全过滤链都通过它代理给FilterChainProxy处理。

多个过滤器的执行顺序由注册顺序决定,也可以用@注解显式指定,数值越小越先执行,这点在实际项目里要留意。

二、Spring过滤器和拦截器的执行顺序
过滤器和拦截器最容易混的地方在于它们都能处理请求,但不在同一个层面上工作。
请求进来的完整链路是这样的,我们从客户端发起的请求先经过过滤器链,再进DispatcherServlet,然后走拦截器的preHandle,接着才到Controller执行业务逻辑。返回的时候反过来——Controller执行完,走拦截器的postHandle,视图渲染完再走afterCompletion,最后出了DispatcherServlet再回过滤器。简单来说,过滤器包在最外层,拦截器夹在DispatcherServlet和Controller中间,两者不是竞争关系,是嵌套关系。

拦截器是Spring自己的东西,基于HandlerInterceptor接口实现,运行在Spring上下文里,能直接拿到Handler信息,也能用Spring的Bean。过滤器没这个条件,它在Servlet层面,是感知不到Spring容器中的资源,处理的是原始的HttpServletRequest和HttpServletResponse。

所以职责上自然也有分工,像编码处理、跨域、日志这类和业务无关的事适合放过滤器。如果是登录校验、权限控制这类需要结合业务逻辑的适合用拦截器。回到一开始说的那个例子,静态资源请求有时候不经过DispatcherServlet,拦截器根本没机会执行,这种场景就只能靠过滤器处理。

以上就是Spring过滤器实现原理,Spring过滤器和拦截器的执行顺序的全部内容了。过滤器是Servlet规范的东西,跑在容器层面,拦截器是Spring自己的,活在DispatcherServlet内部,两者是嵌套关系不是替代关系。执行顺序上,过滤器先动,拦截器后动。选哪个看场景,跟业务无关的用过滤器,需要感知Handler、用Spring Bean的就用拦截器,搞清楚这点基本不会踩坑。