1. 过滤器和拦截器
过滤器(Filter)
拦截器(Interceptor)
1.1 区别
过滤器和拦截器触发时机不一样,
- 过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。
- 请求结束返回也是,是在servlet处理完后,返回给前端之前
拦截器可以获取IOC容器中的各个bean,而过滤器就不行,
- 因为拦截器是spring提供并管理的,spring的功能可以被拦截器使用,在拦截器里注入一个service,可以调用业务逻辑。
- 而过滤器是JavaEE标准,只需依赖servlet api ,不需要依赖spring
过滤器的实现基于回调函数。而拦截器(代理模式)的实现基于反射
Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用
Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理(反射)的方式来执行。
Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此拦截器可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
最简单明了的区别就是:
- 过滤器可以修改request,而拦截器不能
过滤器需要在servlet容器中实现
,拦截器可以适用于javaEE,javaSE等各种环境- 拦截器可以调用IOC容器中的各种依赖,而过滤器不能
- 过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
本质区别:
从灵活性上说拦截器功能更强大些,Filter能做的事情它都能做,而且可以在请求前,请求后执行,比较灵活。
Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验,其他的还是建议用interceptor。
1.2 执行顺序
过滤前-----拦截前-----Action处理-----拦截后-----过滤后。
【过滤器】开始执行
【拦截器】处理前
com.boot.filter.controller.HelloController
Controller:Hello World!
【拦截器】处理中
【拦截器】耗时:20ms
【拦截器】处理后
【拦截器】耗时:20ms
【过滤器】耗时:27ms
【过滤器】结束执行
过滤器(Filter) :可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息
。
拦截器(Interceptor):可以拿到你请求的控制器和方法,却拿不到请求方法的参数
。
切片(Aspect): 可以拿到方法的参数,但是却拿不到http请求和响应的对象
2. 过滤器
过滤器是处于客户端与服务器资源文件之间的一道过滤网
,
在访问资源文件之前,通过一系列的过滤器对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改。也可以对响应进行过滤,拦截或修改响应。
两种实现方式:
1、使用spring boot提供的FilterRegistrationBean注册Filter
2、使用原生servlet注解定义Filter
两种方式的本质都是一样的,都是去FilterRegistrationBean注册自定义Filter
2.1 Filter
2.1.1 示例
2.1.1.1 自定义一个TimeFilter类
@Component
@WebFilter(urlPatterns = {
"/*"})
public class TimeFilter implements Filter{
Logger log = LoggerFactory.getLogger(Logger.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("【过滤器】初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("【过滤器】开始执行");
Long startTime = System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
log.info("【过滤器】耗时:" + (System.currentTimeMillis() - startTime)+"ms");
log.info("【过滤器】结束执行");
}
@Override
public void destroy() {
log.info("【过滤器】销毁");
}
}
2.1.1.2 配置方式
- @WebFilter注解的urlPatterns属性配置了哪些请求可以进入该过滤器,
/*表示所有请求
。 - 除了在过滤器类上加注解外,我们也可以通过FilterRegistrationBean来注册过滤器。定义一个TimeFilterConfig类,加上@Configuration注解表明其为配置类,然后通过FilterRegistrationBean来注册过滤器。
@Configuration public class TimeFilterConfig { @Bean public FilterRegistrationBean timeFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); TimeFilter timeFilter = new TimeFilter(); filterRegistrationBean.setFilter(timeFilter); List<String> urlList = new ArrayList<>(); urlList.add("/*"); filterRegistrationBean.setUrlPatterns(urlList); return filterRegistrationBean; } }
2.1.1.3 测试
@RestController
public class HelloController {
Logger log = LoggerFactory.getLogger(Logger.class);
@RequestMapping("hello")
public void hello(){
log.info("Controller:Hello World!");
}
}
运行结果:
【过滤器】开始执行
Controller:Hello World!
【过滤器】耗时:26ms
【过滤器】结束执行
2.2 OncePerRequestFilter
2.2.1 概念
在Spring中,Filter默认继承OncePerRequestFilter
Filter 可以在 Servlet 执行之前或之后调用。当请求被调度给一个 Servlet 时,RequestDispatcher 可能会将其转发给另一个 Servlet。另一个 Servlet 也有可能使用相同的 Filter。在这种情况下,同一个 Filter 会被调用多次。
OncePerRequestFilter是 Spring 框架中的一个抽象类,
- 用于确保在每个请求中只执行一次过滤操作
- 它通
常用于实现一些需要在每个请求中执行一次的逻辑
,比如日志记录、身份验证等
可以继承 OncePerRequestFilter。
Spring 保证 OncePerRequestFilter 只对指定请求执行一次
。
2.2.1.1 一般只会请求一次,那为什么还要使用该注解
此方法是**为了兼容不同的web container
**,也就是说并不是所有的container都是我们期望的只过滤一次,servlet版本不同,执行过程也不同
- 在servlet2.3中,
Filter会经过一切请求
,包括服务器内部使用的forward转发请求
和<%@ include file=”/login.jsp”%>
的情况 - servlet2.4中的
Filter默认情况下只过滤外部提交的请求
,forward和include这些内部转发都不会被过滤。 - 所以,若是在Spring环境下使用Filter的话,还是继承OncePerRequestFilter吧,而不是直接实现Filter接口
2.2.2 OncePerRequestFilter源码解读
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.request.async.WebAsyncUtils;
public abstract class OncePerRequestFilter extends GenericFilterBean {
// 常量,表示已经过滤的请求属性后缀
public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
// 构造函数
public OncePerRequestFilter() {
}
// 核心过滤方法,确保每个请求只执行一次过滤操作
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 检查请求和响应是否为 HTTP 类型
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取已经过滤的请求属性名称
String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName();
// 检查请求是否已经被过滤
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
// 检查是否需要跳过过滤
if (!this.skipDispatch(httpRequest) && !this.shouldNotFilter(httpRequest)) {
if (hasAlreadyFilteredAttribute) {
// 如果是错误调度类型,执行嵌套错误调度过滤
if (DispatcherType.ERROR.equals(request.getDispatcherType()))