携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情
SpringBoot拦截器(Interceptor)有点类似于Servlet中的过滤器,它主要用于拦截用户发送的请求并进行相应的处理。拦截器可以在项目中进行权限验证、记录请求信息的日志、判断用户是否登录等。过滤器和拦截器在功能上有部分重叠,定义也很相似,一些功能既可以通过过滤器实现,也可以通过拦截器实现,但是它们还是有区别的:
- 拦截器是基于Java的反射机制,而过滤器是基于函数回调。
- 拦截器的使用不依赖于Servlet容器,而过滤器依赖于Servlet容器。
- 拦截器只能对Controller请求起作用,而过滤器则可以对几乎所有的请求(包括静态资源和文件等)起作用。
- 拦截器可以访问请求的上下文、值栈里的对象,而过滤器不能访问。
- 在一个请求的生命周期中,可以设置多个拦截器依次运行,而过滤器只能在容器初始化时被调用一次。
- 拦截器可以获取Spring IoC容器中的各个Bean,而过滤器却获取不到,在拦截器中可以注入业务service,处理业务逻辑。
- 过滤器是在请求Servlet之前拦截请求,对请求进行预处理。请求结束返回也是在Servlet处理完后再返回给前端。而拦截器是在请求处理之前进行拦截处理。
要使用Spring MVC中的拦截器,首先需要对拦截器类进行定义和配置。通常,拦截器类可以通过两种方式来定义。
- 通过实现HandlerInterceptor接口或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义拦截器。
- 通过实现WebRequestInterceptor接口或继承WebRequestInterceptor接口的实现类来定义拦截器,此接口专门用于处理Web请求。
在项目开发中,一个常见的需求就是打印所有的请求入参,方便以后问题的定位和接口的调试。下面我们自定义一个拦截器来处理所有的请求,并且把所有请求的URL和日志都打印出来,具体代码如下:
package com.example.thymeleafdemo.inter;
import lombok.extern.slf4j.Slf4j;
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;
import java.util.Arrays;
import java.util.Map;
/**
* 自定义拦截器
*/
@Slf4j
@Component
public class MyHandlerInterceptor implements HandlerInterceptor {
/**
* 在业务代码处理之前进行参数记录
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServlet
Response response, Object handler) throws Exception {
System.out.println("拦截器: preHandle在控制器的处理请求方法调用之后解析视图之前执行");
String requestURI = request.getRequestURI();
Map<String, String[]> parameterMap = request.getParameterMap();
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
sb.append(entry.getKey()).append("=").append(Arrays.toString
(entry.getValue()));
sb.append(",");
}
log.info("拦截器: 请求的url是:{},请求的参数是:{}",requestURI,sb.toString());
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, HttpServlet Response response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器: afterCompletion方法在控制器的处理请求方法执行完成后执行," + "即视图渲染结束之后执行");
}
}
完成拦截的具体方法后,配置拦截器拦截哪些URL,放行静态资源的请求,拦截剩下的URL。
package com.example.thymeleafdemo.inter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.Interceptor
Registration;
import org.springframework.web.servlet.config.annotation.Interceptor
Registry;
import org.springframework.web.servlet.config.annotation.WebMvc
Configurer;
@Configuration
public class MyHandlerInterceptorConfig implements WebMvcConfigurer {
@Autowired
private MyHandlerInterceptor myHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration = registry.addInterceptor
(myHandlerInterceptor);
//所有路径都被拦截
registration.addPathPatterns("/**");
//添加不拦截路径
registration.excludePathPatterns(
"/**/*.html", //HTML静态资源
"/**/*.js", //JS静态资源
"/**/*.css" //CSS静态资源
);
}
}
启动项目,访问http://localhost:8080/addUser能看到结果页面,如图所示。同时IDEA的控制台中打印出了请求日志,至此已经成功完成了拦截器请求参数的拦截打印。
根据以上自定义拦截器的实现代码,总结拦截器的执行步骤如下:
-
根据请求的URL,找到可以处理请求的处理器和所有拦截器。
-
按照配置顺序执行所有拦截器的preHandle()方法。如果当前拦截器的preHandle()方法返回true,则执行下一个拦截器的preHandle()方法(执行下一个拦截器)。如果当前拦截器返回false,倒序执行所有已经执行了的拦截器的afterCompletion。
-
如果任何一个拦截器返回false,则执行返回,不执行目标方法。
-
所有拦截器都返回true,则执行目标方法。
-
倒序执行所有拦截器的postHandle()方法。
注:前面的步骤有任何异常都会触发倒序执行afterCompletion()方法。
-
页面成功渲染后,再倒序执行afterCompletion()方法。