过滤器和拦截器有什么区别?
- 运行顺序不同:过滤器是在 Servlet 容器接收到请求之后,但在 Servlet被调用之前运行的;而拦截器则是在Servlet 被调用之后,但在响应被发送到客户端之前运行的。
- 过滤器Filter 依赖于 Servlet 容器,属于Servlet规范的一部分,可以在web.xml中进行配置或者使用注解(如
@WebFilter
)进行配置;而 拦截器Interceptor 不依赖于 Servlet 容器,一般可以在Spring配置文件中进行配置或使用注解(如@Interceptor
)来配置拦截器; - Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行;
- Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
知识扩展:
过滤器的使用场景:
- 身份验证:对请求进行身份验证,检查用户是否登录或具有访问权限。
- 请求日志记录:记录请求的详细信息,如URL、参数、请求时间等。
- 数据加工:对请求或响应的数据进行处理,例如压缩、解密、加密等。
- 编码转换:对请求和响应的编码进行转换,以适应不同的字符编码要求。
简单的过滤器代码案例,用于记录请求的访问日志、请求时间记录:
新建LoggingFilter实现Filter类,获取request中的方法、URL,在过滤器链执行完成前后分别记录时间来获取请求处理时间
package com.springboot.learn.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* <p>Class: LoggingFilter</p>
* <p>Description: 日志Filter</p>
*
* @author zhouyi
* @version 1.0
* @date 2023/8/28
*/
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化方法
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long start = System.nanoTime();
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
System.out.println("Request received - Method: " + method + ", URI: " + requestURI + " ,useTime:" + (System.nanoTime() - start));
// 继续执行请求链
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 销毁方法
}
}
在Servlet 中,我们需要在web.xml中配置过滤器,在SpringBoot中没有web.xml的概念,我们通过其他方式配置,如下
方式一:通过FilterRegistrationBean来配置
FilterRegistrationBean
是Spring Boot提供的一个用于注册和配置过滤器的辅助类。它允许你以编程的方式定义过滤器,并将其注册到Servlet容器中。FilterRegistrationBean
提供了一系列方法来配置过滤器的属性,包括过滤器实例、URL匹配模式、过滤器执行顺序、Dispatcher类型等。
通过使用FilterRegistrationBean
,你可以更灵活地配置过滤器,并且不依赖于传统的基于web.xml
配置的方式。你可以将FilterRegistrationBean
作为一个Spring Bean进行管理,可以通过注入方式获取并设置过滤器的属性
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new LoggingFilter());
registration.addUrlPatterns("/*");
registration.setName("LoggingFilter");
registration.setOrder(1);
return registration;
}
}
常见用法
setFilter(Filter filter)
:设置过滤器实例。addUrlPatterns(String... urlPatterns)
:设置URL匹配模式,指定过滤器要拦截的URL路径模式。setOrder(int order)
:设置过滤器的执行顺序,数字越小越先执行。setDispatcherTypes(DispatcherType... dispatcherTypes)
:设置过滤器的Dispatcher类型,指定过滤器要拦截的请求类型,如REQUEST、FORWARD、INCLUDE、ASYNC等。- 其他方法还包括设置过滤器名称、初始化参数、是否启用异步支持等。
功能验证
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/user")
public String queryUser(String id) {
return id;
}
}
访问接口请求,发现控制台已打印出我们想要的信息。
方式二:通过@WebFilter注解实现
在过滤器中添加注解 @WebFilter进行配置,同样可以设置url匹配模式,过滤器名称等。
/**
* <p>Class: LoggingFilter2</p>
* <p>Description: 日志Filter</p>
*
* @author zhouyi
* @version 1.0
* @date 2023/8/28
*/
@WebFilter(urlPatterns = "/*", filterName = "LoggingFilter2")
public class LoggingFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化方法
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long start = System.nanoTime();
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
System.out.println("【LoggingFilter2】Request received - Method: " + method + ", URI: " + requestURI + " ,useTime:" + (System.nanoTime() - start));
// 继续执行请求链
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 销毁方法
}
}
注意,@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包,放在启动类上面。
访问请求,发现过滤器的两种配置方式均已实现
拦截器的使用场景:
- 权限检查:在请求处理方法执行之前,检查用户是否具有执行该操作的权限。
- 日志记录:记录请求的详细信息、处理时间等,用于跟踪和监控。
- 数据转换:在请求处理方法执行前后,对请求或响应的数据进行转换和处理。
- 异常处理:捕获请求处理方法中抛出的异常,并进行相应的处理和响应。
在SpringBoot中实现拦截器有两种方式
(1)实现HandlerInterceptor
接口,并实现相应的方法
(2)继承HandlerInterceptorAdapter
类,并重写里面的方法
方式一:实现HandlerInterceptor
接口
public class LoggingInterceptor implements HandlerInterceptor {
long start = System.nanoTime();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在请求处理方法执行之前的预处理逻辑
start = System.nanoTime();
// 返回 true 表示继续执行后续的拦截器和请求处理方法,返回 false 表示终止执行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在请求处理方法执行之后的后处理逻辑
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
System.out.println("【HandlerInterceptor】Request received - Method: " + method + ", URI: " + requestURI + " ,useTime:" + (System.nanoTime() - start));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在视图渲染完成后的清理逻辑
}
}
配置拦截器
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可添加多个,/**是对所有的请求都做拦截
registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/register");
}
}
运行时发现拦截器也生效
方式二:继承HandlerInterceptorAdapter
类
public class LoggingInterceptor2 extends HandlerInterceptorAdapter {
long start = System.nanoTime();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在请求处理方法执行之前的预处理逻辑
start = System.nanoTime();
// 返回 true 表示继续执行后续的拦截器和请求处理方法,返回 false 表示终止执行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在请求处理方法执行之后的后处理逻辑
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
System.out.println("【LoggingInterceptor2】Request received - Method: " + method + ", URI: " + requestURI + " ,useTime:" + (System.nanoTime() - start));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在视图渲染完成后的清理逻辑
}
}
通过继承HandlerInterceptorAdapter
类来实现拦截器的方式,也需要配置拦截器。和方式一的配置是一样的
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可添加多个,/**是对所有的请求都做拦截
registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/register");
registry.addInterceptor(new LoggingInterceptor2()).addPathPatterns("/**").excludePathPatterns("/login", "/register");
}
}
运行时发现,两种方式的拦截器均已实现。