一、所属技术栈与依赖
过滤器:
是 Servlet 规范的一部分,属于 Java EE 标准 ,依赖于 Servlet 容器。这意味着它主要在 Web 项目的 Servlet 环境中发挥作用,若脱离 Servlet 容器则无法正常运行。
拦截器:
是 Spring MVC 框架提供的功能,属于 Spring 生态的一部分。它不依赖 Servlet 容器,只要存在 Spring 的环境就能使用,并且可以获取 IOC 容器中的各个 bean,方便调用业务逻辑。
二、执行位置与时机
过滤器:
在 Servlet 容器层面工作。在请求进入容器后、到达 Servlet 之前进行预处理;请求结束返回时,在 Servlet 处理完后、返回给前端之前进行后处理 。例如在处理用户请求时,先经过过滤器进行诸如字符编码设置等预处理,再由 Servlet 处理业务逻辑,最后在响应返回前过滤器还可进行响应数据压缩等操作。
拦截器:
在 Spring MVC 的 DispatcherServlet 处理请求的过程中工作。在请求进入 DispatcherServlet 之后、进入具体的处理方法之前进行预处理;在处理方法执行后、视图渲染之前可进行一些操作(如 postHandle 方法) ;在视图渲染完成后还能进行最终处理(如 afterCompletion 方法),并且可以获取到异常信息 。比如在用户请求访问某个 Controller 方法时,拦截器可以先进行权限判断,Controller 方法执行后可以对返回结果做一些调整,视图渲染后进行日志记录等。
三、实现原理
过滤器:
基于回调函数实现。通过实现javax.servlet.Filter
接口,重写init
(初始化)、doFilter
(处理过滤逻辑)、destroy
(销毁)方法。在doFilter
方法中,通过调用FilterChain
对象的doFilter
方法来将请求传递给下一个过滤器或目标资源,是一种基于函数调用链的回调机制 。
拦截器:
基于反射(动态代理)实现。通常实现HandlerInterceptor
接口,重写preHandle
(方法执行前调用)、postHandle
(方法执行后调用)、afterCompletion
(视图页面渲染后调用) 方法。Spring 通过动态代理技术在运行时创建代理对象来调用这些方法,实现对目标方法的拦截和处理 。
四、功能能力与应用场景
过滤器:
- 功能:可以访问原始的 HTTP 请求和响应,也能够修改请求和响应内容 。例如进行敏感词过滤、设置字符集编码、实现 URL 级别的权限访问控制、压缩响应信息等通用功能。
- 应用场景:常用于解决跨域问题(CORS)、统一处理请求的字符编码、对所有请求进行安全相关的前置检查(如防 SQL 注入、防 XSS 攻击等)。
拦截器:
- 功能:能够访问 action 请求上下文、值栈里的对象 ,可以在方法执行的不同阶段进行干预。
- 应用场景:更侧重于业务层面的判断和处理,如登录判断(判断用户是否登录)、权限判断(校验用户是否有权限访问资源)、日志记录(记录请求操作日志,包括用户 IP、访问时间等) 、性能监控(监控请求处理时长)等 。
五、执行顺序与生命周期管理
- 执行顺序:当过滤器和拦截器同时存在时,一般过滤器会先执行,然后才执行拦截器,最后进入真正要调用的方法 。不过具体顺序也可通过各自的配置方式进行调整。
- 生命周期管理:过滤器的生命周期由 Servlet 容器管理,在容器初始化时调用一次初始化方法,后续每次请求按规则调用过滤方法,容器销毁时调用销毁方法 。拦截器则可以通过 IoC 容器来管理,可通过注入等方式获取其他 Bean 的实例,使用相对更灵活 。
六、代码实现
过滤器
过滤器的简单实现有两个步骤
1.创建过滤器,实现filter接口,重写方法,再过滤器类上添加@WebFilter注解,指定要拦截的url
2.开启注解扫描,在启动类上加@ServletComponentScan注解,扫描被@WebFilter标记的类
简单写个测试,controller层
package com.dong.filterandinterceptor.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class FilterTest {
@RequestMapping("/testFilter")
public String test(){
log.info("执行业务代码....");
return "success";
}
}
创建过滤器,拦截所有请求
package com.dong.filterandinterceptor.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(value = {"/*"})
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//过滤前执行
System.out.println("MyFilter before...");
//执行业务逻辑,决定是否放行
chain.doFilter(request, response);
//过滤后执行
System.out.println("MyFilter after...");
}
}
启动类上别忘了加@ServletComponentScan注解
package com.dong.filterandinterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class FilterAndInterceptorApplication {
public static void main(String[] args) {
SpringApplication.run(FilterAndInterceptorApplication.class, args);
}
}
访问localhost:8088/testFilter,可以看到在进入controller层先进入过滤器中
拦截器
拦截器要比过滤器步骤多一点
1.创建拦截器类,实现HandlerInterceptor接口,在该类上标记@Component注解,纳入IOC容器
2.创建配置类,实现WebMvcConfigurer接口,在该类上加上@Configuration注解,标记配置类
拦截器,在过滤器放行后执行到达拦截器的preHandle方法,返回true,才到达Controller层
业务执行完之后,执行拦截器的postHandle方法,然后再是afterCompletion方法,配置了过滤器的话,最后就执行过滤器doFilter方法后的代码
package com.dong.filterandinterceptor.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class MyInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor before...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor after...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor afterCompletion...");
}
}
配置类,将创建的拦截器加入拦截器链中,拦截所有请求,这里与过滤器不同,是 /**
package com.dong.filterandinterceptor.config;
import com.dong.filterandinterceptor.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
//配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor).addPathPatterns("/**");
}
}
执行结果,这里我过滤器和拦截器都配置了
这是过滤器和拦截器同时存在的流程图
还有更复杂的用法,配置了多个拦截器,他们之间的执行顺序又会有所不同
小总结:过滤器的拦截范围要比拦截器大,过滤器拦截所有浏览器输入的所有请求,而拦截器仅针对controller上拦截的url资源。