拦截器
拦截器是什么
拦截器(Interceptor)是一种特殊的组件,它是基于反射
进行实现。它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截的是servlet 和 controller 之间的请求和响应。
拦截器的使用
-
权限控制:拦截器可以在请求
到达处理器之前
进行权限验证,从而实现对不同用户的访问控制。 -
日志记录:拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试。
-
接口幂等性校验:拦截器可以在
请求到达处理器
之前进行幂等性校验,防止重复提交。 -
数据校验:拦截器可以在
请求到达处理器之前
对请求数据进行校验 ,确保数据的合法性。 -
缓存处理:拦截器可以在
请求处理之后
对响应数据进行缓存,提高系统性能。
拦截器的实现
导入依赖
如果是使用的是springboot项目的话,直接导入这个依赖,spring项目需要导入 web 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
实现Handlerlnterceptor接口
要在SpringBoot中实现拦截器,首先需要创建一个类并 实现HandlerInterceptor
接口。HandlerInterceptor接口包含以下三个方法:
-
preHandle:在
请求到达处理器之前
执行,可以用于权限验证、数据校验等操作。如果返回true,则继续执行后续操作
;如果返回false,则中断请求处理
。 -
postHandle:在
处理器处理请求之后
执行,可以用于日志记录、缓存处理等操作。 -
afterCompletion:在
视图渲染之后
执行,可以用于资源清理等操作。
public class MainInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("我是处理之前!");
return true; //只有返回true才会继续,否则直接结束
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("我是处理之后!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("我是完成之后!");
}
}
注册拦截器
要让拦截器生效,需要将其注册到InterceptorRegistry中。这可以通过实现WebMvcConfigurer接口并重写addInterceptors
方法来实现。注册成功以后,我们还可以设置拦截的规则,拦截的路径,和不拦截的路径
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private MainInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器,并设置拦截路径
registry.addInterceptor(customInterceptor)
.addPathPatterns("/**").excludePathPatterns("/exclude/**");
}
}
拦截器的生命周期
拦截器的执行顺序
当有多个拦截器时,它们的执行顺序取决于注册顺序。先注册的拦截器先执行,后注册的拦截器后执行。在请求处理过程中,拦截器的preHandle方法按注册顺序
执行,而postHandle和afterCompletion方法按注册顺序的逆序
执行。
拦截器的生命周期
拦截器的生命周期由Spring容器管理。当Spring容器启动时,拦截器会被实例化并初始化;当Spring容器关闭时,拦截器会被销毁
多个拦截器的执行流程
当有多个拦截器时,它们的执行流程如下:
-
执行所有拦截器的preHandle方法,按注册顺序执行。如果某个拦截器的preHandle方法返回false,则中断请求处理,直接执行已执
-
拦截器的
afterCompletion
方法。 -
执行处理器的处理方法。
-
执行所有拦截器的
postHandle
方法,按注册顺序的逆序
执行。 -
渲染视图。
-
执行所有拦截器的
afterCompletion
方法,按注册顺序的逆序执行。
拦截器的实际使用
拦截器实现日志记录
使用拦截器记录接口访问的记录,记录访问该接口的IP地址
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("request.getRemoteHost() = " + request.getRemoteHost());
System.out.println("request.getParameter(\"id\") = " + request.getParameter("id"));
return true; //只有返回true才会继续,否则直接结束
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
request.getParameter("id");
System.out.println("我是处理之后!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("我是完成之后!");
}
}
实现接口幂等性校验
拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。以下是一个简单的幂等性校验示例:
public class IdempotentInterceptor implements HandlerInterceptor {
private static final String IDEMPOTENT_TOKEN = "idempotentToken";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader(IDEMPOTENT_TOKEN);
if (StringUtils.isEmpty(token)) {
throw new RuntimeException("Idempotent token is missing");
}
if (!checkIdempotentToken(token)) {
throw new RuntimeException("Duplicate request");
}
return true;
}
private boolean checkIdempotentToken(String token) {
// Check the token in the cache or database
// Return true if the token is valid, false otherwise
}
}
//在上述示例中,我们在preHandle方法中检查请求头中的幂等性令牌,如果令牌无效,则抛出异常并中断请求处理。
拦截器的性能优化
拦截器在请求处理过程中可能会影响系统性能,以下是一些性能优化策略:
-
减少拦截器数量:尽量将相关功能集中到一个拦截器中,避免创建过多的拦截器。
-
精确配置拦截规则:通过
addPathPatterns
和excludePathPatterns
方法精确配置拦截规则,避免不必要的拦截。 -
使用异步处理:在拦截器中使用异步处理,避免阻塞请求处理过程。
-
使用缓存:在拦截器中使用缓存,减少对数据库或其他资源的访问。
过滤器
过滤器是什么
过滤器顾名思义就是对事物进行过滤的,在Web中的过滤器,当然就是对请求进行过滤,我们使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。过滤器的实现是通过函数回调
进行实现。
函数回调:它指的是将一个函数作为参数
传递给另一个函数,并在特定事件发生时被调用执行的函数。这种方式使得在异步编程中,当某个操作完成后,系统能够调用预先定义好的回调函数来处理结果,从而避免阻塞程序的执行,提高程序的效率和响应速度。
过滤器的使用
- 如登录控制
- 权限管理
- 过滤敏感词汇等
过滤器的实现
过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter
注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。
创建过滤器
init()
:该方法在容器启动初始化
过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。 FilterConfig可以获取配置信息
doFilter()
:容器中的 每一次请求
都会调用该方法 , FilterChain 用来调用下一个过滤器 Filter。
默认就会传入Request
和Response
,这个参数封装了请求和响应,我们直接使用就行。ServletResquest和ServletResponse可以直接强转成HttpServletRequest和HttpServletResponse,然后使用相应的方法。
destroy()
: 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
@Component
@WebFilter("/*") // 定义的过滤规则,只过滤对应的url请求
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter 前置");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter 处理中");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("Filter 后置");
}
}
开启URL过滤
开启专门的Url过滤需要在启动类添加一个注解 @ServletComponentScan
@SpringBootApplication
@ServletComponentScan
public class InterceptorApplication {
public static void main(String[] args) {
SpringApplication.run(InterceptorApplication.class, args);
}
}
过滤器的生命周期
-
初始化阶段(Initialization): 在 Servlet 容器启动时,会实例化所有的过滤器并调用其 init 方法进行初始化。在 init 方法中,过滤器可以进行一些初始化操作,例如读取配置文件、建立数据库连接等。该方法只会在过滤器实例化时调用一次。
-
请求处理阶段(Request Processing): 在请求到达 Servlet 前,过滤器可以对请求进行预处理。当请求与过滤器匹配时,Servlet 容器会调用过滤器的 doFilter 方法来处理请求。在 doFilter 方法中,过滤器可以执行一些操作,例如修改请求或响应内容、记录日志、验证权限等。如果请求不符合过滤器的条件,过滤器可以选择放行请求,将请求传递给下一个过滤器或目标 Servlet。
-
销毁阶段(Destruction): 在 Servlet 容器关闭时,会销毁所有的过滤器并调用其 destroy 方法进行清理。在 destroy 方法中,过滤器可以进行一些清理操作,例如释放资源、关闭连接等。该方法只会在过滤器销毁时调用一次。
多个Filter的执行顺序
-
如果我们是在web.xml中配置的过滤器,那么过滤器的执行顺序就是
在web配置的顺序,配置在上面那么就会先执行。 -
如果我们是使用
@WebFilter
进行配置的,那么执行顺序就是字符比较顺序来执行,例如有2个过滤器,一个是AFilter,一个是BFilter,那么AFilter就会先执行。 -
如果注解和xml混用,那么在web.xml中配置的会先执行。
过滤器和拦截器的区别
异同
二者都是体现了AOP的思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的,接下来一一说明。
底层原理
拦截器是使用反射
进行实现,过滤器是基于函数回调
进行实现
使用范围不同
-
过滤器 实现的是
javax.servlet.Filter
接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。 -
拦截器(Interceptor) 它是一个
Spring组件
,并由Spring容器管理
,并不依赖Tomcat等容器,是可以单独使用的
。不仅能应用在web程序中,也可以用于Application、Swing等程序中。
触发时机不同
触发时机不同
过滤器 和 拦截器的触发时机也不同,我们看下边这张图
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
控制执行的顺序不同
拦截器:是先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行 为什么?
两个方法中在调用拦截器数组 HandlerInterceptor[] 时,循环的顺序竟然是相反的
。导致postHandle()、preHandle() 方法执行的顺序相反。
评论( 0 )