源码解析
版本信息:SpringBoot 2.6.1
1. 拦截器源码解析
1.1 测试 controller 编写
@RestController
@Slf4j
public class TestController {
@GetMapping("/user/{id}")
public User user(@PathVariable Integer id) {
log.info("查询用户信息id={}", id);
User user = new User();
user.setName("张三");
user.setAge(18);
return user;
}
}
1.2 拦截器编写
自定义一个拦截器,拦截的方法中我们就简单的打印一句话
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("执行preHandle....");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("执行postHandle....");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("执行afterCompletion....");
}
}
1.3 添加拦截器
设置规则为拦截所有 /user 开头的请求
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/**");
}
}
1.4 debug 源码
接下来我们用 postman 发起请求来看一下拦截器的处理过程
对于web请求的处理流程我们直接从 org.springframework.web.servlet.DispatcherServlet#doDispatch 开始看
再获取到请求处理器适配器之后,我们直接来到 applyPreHandle()
顺序遍历所有的拦截器执行他们的 preHandle() 方法,如果没有放行就会执行 triggerAfterCompletion()
注意如果执行了triggerAfterCompletion(),内部会倒序执行每一个拦截器的 afterCompletion()
执行完前置处理器后接下来就是执行目标方法
接下来就是执行 applyPostHandle()
注意此时是倒序执行所有拦截器的 postHandle()
接下来执行 processDispatchResult() 处理请求结果
在这个方法中执行 triggerAfterCompletion()
注意这里是倒序执行所有已执行拦截器的 afterCompletion() 方法
如果在请求过程中出现了异常,也会执行所有已执行拦截器的 afterCompletion()
本次 debug 涉及的关键断点如下图所示
2. 总结
- 顺序执行所有拦截器preHandle(),返回false倒序执行已执行preHandle()的拦截器的 afterCompletion()
- 执行目标方法
- 倒序执行所有拦截器的 postHandle()
- 倒序执行所有拦截器的 afterCompletion()
- 期间出现任何异常都会倒序执行已执行preHandle()的拦截器的 afterCompletion()