目录
一、拦截器
拦截器是什么
拦截器(Interceptor)是一种特殊的组件,它是基于反射进行实现。它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截的是servlet 和 controller 之间的请求和响应。
拦截器的使用
-
权限控制:拦截器可以在请求到达处理器之前进行权限验证,从而实现对不同用户的访问控制。
-
日志记录:拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试。
-
接口幂等性校验:拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。
-
数据校验:拦截器可以在请求到达处理器之前对请求数据进行校验,确保数据的合法性。
-
缓存处理:拦截器可以在请求处理之后对响应数据进行缓存,提高系统性能。
拦截器的实现
导入依赖
如果是使用的是springboot项目的话,直接导入这个依赖,spring项目需要导入 web 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
实现HandlerInterceptor
接口
要在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中配置的过滤器,那么过滤器的执行顺序就是<filter-mapping>在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()
方法执行的顺序相反。