上一篇文章讲了监听器Listener,下面我们来讲一下过滤器和拦截器。
一、过滤器Filter。
首先,servlet容器(比如tomcat)肯定的要有servlet才能发挥它的光彩。在上古jsp时代,我们会写各种servlet通过不同的请求来实现我们相关的业务,就如同我们现在要写各种controller一样来接收用户请求一样。现在我们不需要写servlet了,因为伟大的spring拯救了我们这些copy工程师,也因为spring的易用的良好生态导致我们很多人现在找不到活。spring给我们弄了个很牛X的servlet,那就是DispatcherServlet。 我们老程序员肯定看过如下配置:
<!-- web.xml -->
<web-app>
<!-- ... -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 可选配置:设置Spring MVC配置文件的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
</init-param>
<!-- 可选配置:设置DispatcherServlet的加载顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- 配置DispatcherServlet处理哪些请求 -->
<url-pattern>/</url-pattern> <!-- 通常配置为'/'来处理所有请求 -->
</servlet-mapping>
<!-- ... -->
</web-app>
我们Java程序员老是自豪的喊我们写的web是MVC的,大部分依赖于DispatcherServlet它这个家伙。
DispatcherServlet 是 Spring Web MVC 框架的核心组件,它充当了前端控制器的角色,实现了前端控制器设计模式。在基于 Spring MVC 构建的 Java Web 应用程序中,DispatcherServlet 主要承担以下作用:
-
统一入口:作为所有HTTP请求的集中处理者,应用程序的所有请求首先都会被路由到DispatcherServlet。
-
请求分发:根据用户请求的URL,DispatcherServlet会查找合适的处理器(Controller),这一过程通过HandlerMapping实现,它可以将请求映射到具体的处理器方法。
-
处理器适配:找到处理器后,DispatcherServlet使用HandlerAdapter来调用处理器的方法执行业务逻辑,即使处理器有不同的实现方式(如注解控制器、XML配置的控制器等)。
-
数据绑定和验证:在调用处理器方法前,DispatcherServlet会自动将请求参数绑定到处理器方法的参数上,并执行任何相关的数据验证。
-
视图解析:处理器方法执行完毕后,通常会返回一个逻辑视图名,DispatcherServlet通过ViewResolver接口将逻辑视图名解析成实际的视图对象,进而渲染模型数据并生成最终的响应内容。
-
拦截器处理:支持HandlerInterceptor(拦截器)的配置,可以在请求处理前后执行额外的逻辑,例如权限检查、日志记录等。
-
异常处理:处理控制器抛出的异常,并根据配置将其映射到特定的错误页面或返回适当的HTTP错误状态码。
-
文件上传支持:通过配置MultipartResolver,DispatcherServlet能处理多部分表单提交,如文件上传请求。
-
国际化支持:支持对请求进行本地化解析,确保返回的内容符合用户的语言环境。
DispatcherServlet在整个Spring MVC架构中扮演着核心调度和管理角色,它负责协调各个组件以完成HTTP请求的处理并返回响应。通过这种方式,它简化了Web应用程序的设计,促进了模块化和可扩展性。
回归正题,在请求没有到servlet之前或者请求离开了servlet之后,想要干点什么该怎么做呢?其实这个想法在没有spring之前就有古老程序员想到了,比如他们遇到了比如说刚写好了一个销售新研发的薄荷味儿的六味地黄丸的servlet,但是太医院御医突发奇想拍脑门说我要跟踪所有访问过这个servlet的贝勒爷有哪些,但工部侍郎不太想改刚写好的servlet,万一增加了这个功能,明天八旗子弟不愿意了又让去掉。。。
这时候过滤器Filter就登场了,它与servlet非常类似,过滤器就是Java组件,请求发送到servlet之前,可以用过滤器截获和处理请求,另外servlet结束工作之后,但在响应发回给客户之前,可以用过滤器处理响应。过滤器可以链到一起,一个接一个地运行。过滤器设计为完全自包含的。过滤器并不关心在它前面运行了哪些过滤器(如果有的话),也不关心后面还会运行哪个过滤器。
过滤器与servlet很像,过滤器有自己的API。如果一个Java类实现了Filter接口对容器来说就有很大不同,这个类会从原先一个普通的Java类摇身一变而成为一个正式的J2EE过滤器。过滤器API的其他成员允许过滤器访问ServletContext,而且可以与其他过滤器链接。就像servlet一样,过滤器也有一个生命周期。类似于servlet,过滤器有init()和destroy()方法。对应于servlet的doGet()/doPost()方法,过滤器则有一个doFilter()方法。Web应用可以有很多的过滤器,一个给定请求可能导致执行多个过滤器。针对请求要运行哪些过滤器,以及运行的顺序如何,这些都要在DD中声明。
真正的工作在doFilter()中完成,每次容器认为应该对当前请求应用过滤器时,就会调用doFilter()方法。
doFilterO方法有3个参数:一个ServletRequest(而不是HttpServletRequest),一个ServletResponse(而不是HttpServletResponse),一个FilterChain。
它在古老的定义里是这样的。
那么他就能在servlet执行前后干一些别的事情了。比如:
-
自定义Filter: 开发者可以编写自己的Filter实现,例如认证过滤器(Authentication Filter)、授权过滤器(Authorization Filter)、字符编码过滤器(Character Encoding Filter)、日志记录过滤器(Logging Filter)等,用于实现特定的请求预处理和后处理功能。
-
Spring Security Filter Chain: Spring Security框架提供了一整套安全过滤器链,其中包括多个内置的Filter,如
ChannelProcessingFilter
(通道处理过滤器)、SecurityContextPersistenceFilter
(安全上下文持久化过滤器)、LogoutFilter
(注销过滤器)、UsernamePasswordAuthenticationFilter
(用户名密码认证过滤器)等,这些过滤器一起协作保障了应用的安全性。 -
Cross-Origin Resource Sharing (CORS) Filter: 为了支持跨域资源共享,开发者经常编写CORS Filter来处理浏览器的预检请求OPTIONS方法以及设置响应头以允许跨域访问。
-
GZIP Compression Filter: 用于压缩HTTP响应内容,提升网络传输效率。
-
Content-Type and Character Encoding Filter: 设置响应的Content-Type和字符编码。
-
Static Resources Filter: 对静态资源(如CSS、JavaScript、图片等)的访问进行优化处理,如缓存控制。
-
Locale Change Filter: 用于处理用户地域变更请求,切换应用的语言环境。
在Spring Boot项目中,可以通过实现javax.servlet.Filter
接口并配置到Spring的WebApplicationInitializer或通过@WebFilter
注解在类上声明的方式创建自定义Filter,并在Spring的配置文件或Java配置类中注册到Servlet容器的过滤器链中。特别是你在学Spring Security的时候会发现有很多filter。
二、拦截器Interceptor。
拦截器(Interceptor)和过滤器(Filter)是Java Web框架中两种不同的设计模式,它们各自独立,并非基于彼此设计,但常常在功能上有协同作用,共同完成对HTTP请求和响应的预处理和后处理工作。
过滤器上面说了,是Servlet规范的一部分,它基于责任链模式,能够对Web容器接收到的所有请求及其对应的响应进行统一处理,例如权限验证、编码转换、压缩等。过滤器按照定义的顺序链式执行,且必须依赖于Servlet容器。
拦截器则是更偏向于具体框架的概念,比如Spring MVC框架中的拦截器,它是基于面向切面编程(AOP)思想设计的,通常利用Java反射机制实现,主要用于处理控制器层(如Action、Controller)的请求和响应。拦截器可以介入到业务逻辑执行前后,进行诸如身份验证、日志记录、性能监控等操作。
虽然拦截器和过滤器都可以实现类似的功能,比如对请求进行预处理和后处理,但它们的设计原理、使用场景以及所在的层次有所不同,在实际项目中,可以根据需求选择使用过滤器、拦截器或同时使用两者来优化应用的功能结构。
在Spring框架中,特别是在Spring MVC环境下,Interceptor(拦截器)主要用于增强或修改请求处理的行为,提供一种灵活的方式来添加通用的横切关注点。比如权限验证、日志记录、性能监控、事务管理等。Spring MVC中主要的Interceptor类型如下:
-
HandlerInterceptor: 这是最常见的Spring MVC拦截器类型,通过实现
org.springframework.web.servlet.HandlerInterceptor
接口来创建自定义的拦截器。HandlerInterceptor有三个方法可以覆盖:preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
:在处理器执行之前调用,可以决定是否继续执行后续的处理流程。postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
:在处理器执行完并且视图渲染之前调用,可以修改ModelAndView对象。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
:在整个请求处理完成后(包括视图渲染)调用,主要用于清理资源等工作。
-
WebRequestInterceptor: 实现
org.springframework.web.context.request.WebRequestInterceptor
接口,这种拦截器针对的是所有的Web请求,不仅仅是HTTP请求,它的方法包括:preHandle(WebRequest request)
:在请求处理之前调用。postHandle(WebRequest request, ModelMap model)
:在请求处理完成但视图尚未渲染之前调用。afterCompletion(WebRequest request, Exception ex)
:在请求处理完全结束后调用,无论是否有异常。
-
AsyncHandlerInterceptor: 用于异步请求处理的拦截器,继承自
HandlerInterceptor
接口,增加了对异步请求生命周期的支持。
除了上述标准拦截器,Spring还提供了许多与特定功能相结合的拦截器,例如:
HandlerExceptionResolver
接口及其实现类,用于异常处理的拦截,虽然不是严格意义上的Interceptor,但在请求处理流程中起到类似的作用。
此外,Spring AOP也可以看作是一种广义的拦截器,它可以应用于整个Spring容器中所有bean的方法调用,实现更为广泛的切面编程,但与Spring MVC中的HandlerInterceptor不同,它更多的是面向服务和业务层的增强。
那么拦截器和过滤器怎么选择呢?老祖宗在我们面向对象的同时又给我们搞出来一个面向切面的利器,那就是AOP,比如我们可以实现Spring MVC中的org.springframework.web.servlet.HandlerInterceptor拦截器接口。
该接口定义了三个方法,分别对应请求处理的不同阶段,能让我们直接一砍刀切到我们喜欢的Bean中去 :
-
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
:在处理器执行前调用,如果该方法返回false,则后续的处理器不会被执行,通常用于权限验证、登录状态检查等预处理操作。 -
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
:在处理器执行完业务逻辑后,视图渲染前调用,可以修改ModelAndView对象,影响视图呈现的数据。 -
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
:在整个请求处理完成后(包括视图渲染)调用,用于释放资源、记录日志等收尾工作。
Spring通过HandlerMapping和HandlerExecutionChain维护一系列的拦截器,形成拦截器链(Interceptor Chain),当请求到达DispatcherServlet时,会沿着这个链依次调用每个拦截器的相应方法。
你是不是感觉拦截器和过滤器功能感很像呢,又是pre又是post,对象的前后都能搞而且都带责任链?是不是又觉得拦截器更牛B想搞哪个对象搞哪个对象呢?那我们该怎么选择呢?因为拦截器需要切到Bean中去,如果你明确知道要搞的那些Bean去增强它,那么你可以用拦截器Interceptor。如果你想再请求前后对某些请求统一做操作并不关心后面某某某实现的某些bean,那么你就用Filter。使用范围和应用场景上的不同点:
-
实现原理与依赖
- Filter: 是Java Servlet规范的一部分,基于Servlet容器提供的过滤器接口
javax.servlet.Filter
实现。Filter的执行由Servlet容器负责调用,对所有进入Servlet容器的HTTP请求均能进行拦截。 - Interceptor: 是Spring MVC框架提供的功能,基于Spring的
org.springframework.web.servlet.HandlerInterceptor
接口实现。Interceptor的执行由Spring DispatcherServlet控制,仅对经过Spring MVC框架处理的请求起作用。
- Filter: 是Java Servlet规范的一部分,基于Servlet容器提供的过滤器接口
-
使用范围
- Filter: 可以应用于任何基于Servlet的Web应用程序,不限于Spring MVC。它可以捕获所有的HTTP请求和响应,包括静态资源请求和其他框架处理的请求。
- Interceptor: 特别针对Spring MVC框架中的Controller请求处理,无法直接拦截静态资源请求或非Spring MVC处理的请求。
-
执行顺序
- Filter: 执行顺序较早,处于请求到达Spring DispatcherServlet之前,可以对请求进行初步处理,如编码转换、安全检查等。
- Interceptor: 执行顺序在Filter之后,仅针对Spring MVC Controller的请求处理,发生在Controller方法调用前后以及视图渲染前后。
-
功能定位
- Filter: 一般用于处理跨越多个应用或框架的通用问题,如登录验证、日志记录、编码转换、请求压缩等,这些处理往往不需要知道具体的业务逻辑。
- Interceptor: 更加贴近业务逻辑,通常用于处理与Controller方法执行紧密相关的任务,如权限验证、性能监控、统一异常处理、事务管理、注入额外数据到ModelAndView等。
-
调用时机
- Filter: 可以在请求之前(
doFilter()
方法中)、请求之后或整个请求生命周期的任意时刻进行处理。 - Interceptor: 有明确的三个调用点:
preHandle()
(前置处理,可决定是否继续执行请求)、postHandle()
(后置处理,可以修改模型和视图数据)和afterCompletion()
(完全完成,通常用于清理资源)。
- Filter: 可以在请求之前(
总结起来Filter在Web请求处理的早期阶段发挥作用,拥有更广泛的适用范围,而Interceptor则更聚焦于Spring MVC框架内部的请求处理环节,能够深度参与到业务逻辑的执行过程中。两者结合使用,可以为Web应用提供全面且细致的请求处理和响应控制。