拦截器(Interceptor)是一种特殊的组件,它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截器可以在请求到达目标处理器之前、处理器处理请求之后以及视图渲染之前执行特定的操作。拦截器的主要目的是在不修改原有代码的情况下,实现对请求和响应的统一处理。
在 Spring Boot 中拦截器的实现分为两步:
- 创建一个普通的拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法;
- 将上一步创建的拦截器加入到 Spring Boot 的配置文件中,并配置拦截规则。
自定义拦截器
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("拦截器:执行 preHandle 方法。");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器:执行 postHandle 方法。");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器:执行 afterCompletion 方法。");
}
}
- preHandler 方法:在请求方法执行前被调用,也就是调用目标方法之前被调用。比如我们在操作数据之前先要验证用户的登录信息,就可以在此方法中实现,如果验证成功则返回 true,继续执行数据操作业务;否则就返回 false,后续操作数据的业务就不会被执行了。
- postHandle 方法:调用请求方法之后执行,但它会在 DispatcherServlet 进行渲染视图之前被执行。
- afterCompletion 方法:会在整个请求结束之后再执行,也就是在 DispatcherServlet 渲染了对应的视图之后再执行
将自定义拦截器配置到系统设置中,并且设置拦截规则
- 实现 WebMvcConfigurer,表明是一个系统的配置文件
- 重写 addInterceptors 方法(支持添加多个拦截器)
package com.example.demo.config;
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 AppConfig implements WebMvcConfigurer {
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//registry.addInterceptor(new UserInterceptor());
registry.addInterceptor(userInterceptor)
.addPathPatterns("/**")//拦截规则:/**拦截所有的请求
.excludePathPatterns("/user/reg")//不拦截
.excludePathPatterns("/user/login");
}
}
拦截规则:
- addPathPatterns:表示需要拦截的 URL
- /**:表示拦截任意方法(也就是所有方法)
- excludePathPatterns:表示需要排除的 URL
如果要排除所有静态资源:在这里有一个问题就是排除图片,图片的格式有很多,总不能一个一个排除,这样的情况我们可以在 resource 下的 static 创建一个 image 目录,把所有图片都放在这里边,排除 image 里边的所有东西
.excludePathPatterns("/image/**");
拦截器实现原理
没有拦截器的的时候:用户调用来到后端程序,请求提交给Controller,Controller 处理请求访问 Service,Service 访问 Mapper,Mapper 访问数据库,然后数据库把结果在一层一层返回给用户
加拦截器:此时用户调用来到后端程序,先会来到拦截器进行预处理——返回 true,执行后边流程;返回 false,则返回非权限给页面
加拦截器代码原理:所有的 Controller 执行都会通过⼀个调度器 DispatcherServlet 来实现(而所有方法都会执行 DispatcherServlet 中的 doDispatch 调度方法,其中会执行 applyPreHandle 方法。这个方法含义是:执行所有的拦截器,所有拦截器不等于 false,才会进行之后的代码)
过滤器实现
过滤器,顾名思义就是对事物进行过滤的,在Web中的过滤器,当然就是对请求进行过滤,我们使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。如登录控制,权限管理,过滤敏感词汇等。
过滤器可以使用 Servlet 3.0 提供的 @WebFilter 注解,配置过滤的 URL 规则,然后再实现 Filter 接口,重写接口中的 doFilter 方法,具体实现代码如下:
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Component
@WebFilter(urlPatterns = "/*")
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器:执行 init 方法。");
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器:开始执行 doFilter 方法。");
// 请求放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("过滤器:结束执行 doFilter 方法。");
}
@Override
public void destroy() {
System.out.println("过滤器:执行 destroy 方法。");
}
}
- init 方法:容器启动(初始化 Filter)时会被调用,整个程序运行期只会被调用一次。用于实现 Filter 对象的初始化。
- doFilter 方法:体的过滤功能实现代码,通过此方法对请求进行过滤处理,其中 FilterChain 参数是用来调用下一个过滤器或执行下一个流程。
- destory 方法:用于 Filter 销毁前完成相关资源的回收工作。
过滤器原理
当我们使用过滤器时,过滤器会对浏览器的请求进行过滤,过滤器可以动态的分为3个部分:1.放行之前的代码,2.放行,3.放行后的代码,这3个部分分别会发挥不同作用。
- 第一部分代码会对浏览器请求进行第一次过滤,然后继续执行
- 第二部分代码就是将浏览器请求放行,如果还有过滤器,那么就继续交给下一个过滤器
- 第三部分代码就是对返回的Web资源再次进行过滤处理
拦截器和过滤器区别
- 出身不同:过滤器来自于 Servlet,而拦截器来自于 Spring 框架;
- 触发时机不同:请求的执行顺序是:请求进入容器 > 进入过滤器 > 进入 Servlet > 进入拦截器 > 执行控制器(Controller),所以过滤器和拦截器的执行时机,是过滤器会先执行,然后才会执行拦截器,最后才会进入真正的要调用的方法
- 底层实现不同:过滤器是基于方法回调实现的,拦截器是基于动态代理(底层是反射)实现的;
- 支持的项目类型不同:过滤器是 Servlet 规范中定义的,所以过滤器要依赖 Servlet 容器,它只能用在 Web 项目中;而拦截器是 Spring 中的一个组件,因此拦截器既可以用在 Web 项目中,同时还可以用在 Application 或 Swing 程序中;
- 使用的场景不同:因为拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:登录判断、权限判断、日志记录等业务;而过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、字符集编码设置、响应数据压缩等功能。