Spring MVC 中所有的拦截器都实现/继承自 HandlerInterceptor
接口。
HandlerInterceptor
接口的源码如下:
public interface HandlerInterceptor {
// 在请求处理之前进行调用(Controller方法调用之前)
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 在整个请求结束之后被调用,也就是在 DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
自定义拦截器案例:
@Slf4j
@Component
public class TimeInterceptor implements HandlerInterceptor {
private ThreadLocal<Long> threadLocalStart = new ThreadLocal<>();
private ThreadLocal<Long> threadLocalEnd = new ThreadLocal<>();
// 记录开始时间
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTIme = System.currentTimeMillis();
threadLocalStart.set(startTIme);
log.info("开始时间:{}", startTIme);
return true;
}
// 记录结束时间
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long endTIme = System.currentTimeMillis();
threadLocalEnd.set(endTIme);
log.info("结束时间:{}", endTIme);
}
// 计算接口执行时间
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long startTime = threadLocalStart.get();
long endTime = threadLocalEnd.get();
log.info("接口执行时间:{} 毫秒", endTime - startTime);
}
}
拦截器注册:
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(timeInterceptor);
// 拦截所有请求
registration.addPathPatterns("/**");
// 添加不拦截路径
registration.excludePathPatterns("/login", "/error", "/logout","/login.html");
}
}
1、执行 preHandle 方法,该方法会返回一个布尔值。如果为 false ,则结束本次请求:如果为 true 则继续。
2、执行处理器逻辑,也就是我们的 Controller 。
3、执行 postHandle 方法。
4、执行视图解析和视图渲染 (直接返回了 JSON 对象,所以没有视图处理)。
5、执行 afterCompletion 方法。
可以在 DispatcherServlet
的 doDispatch
方法的源码中进一步验证这个执行逻辑:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
try {
// 返回 HandlerExecutionChain 其中包含了拦截器队列
mappedHandler = getHandler(processedRequest);
//调用拦截器 PreHandle 方法,若返回 false 将直接 return
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 处理 Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 调用拦截器的 PostHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 调用拦截器的 afterCompletion 方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}