拦截器
Spring MVC中所有的拦截器都实现/继承自HandlerInterceptor接口。自定义拦截器,就需要实现/继承HandlerInterceptor接口或其子接口/实现类。下图为Spring MVC中拦截器的类图。
HandlerInterceptor接口的源码:
public interface HandlerInterceptor {
//处理器执行前被调用
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//处理器执行后、视图渲染前被调用
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
//视图渲染完成后调用
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
拦截器的执行流程:
新建一个LogInterceptor类并实现HandlerInterceptor接口:
public class LogInterceptor 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");
}
}
新建一个WebConfigurer类并实现WebMvcConfigurer接口,用于注册自定义的拦截器:
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
在HelloController类的hello方法中添加一条日志打印代码:
public class HelloController {
@GetMapping("/hello")
public String hello() {
log.info("hello");
return "Hello Spring Boot";
}
}
启动工程,访问http://localhost:8080/hello,会在控制台看到如下输出:
多个拦截器的执行顺序:
preHandle方法与afterCompletion方法总是成对出现的,一个拦截器的preHandle方法被调用后,afterCompletion方法必然也会被调用。
再创建一个拦截器,用来记录程序执行消耗的时间,并创建一个TimeInterceptor类,同样实现HandlerInterceptor接口,代码如下:
public class TimeInterceptor implements HandlerInterceptor {
private final ThreadLocal<LocalTime> threadLocalStart = new ThreadLocal<>();
private final ThreadLocal<LocalTime> threadLocalEnd = new ThreadLocal<>();
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
// 记录开始时间
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
LocalTime startTime = LocalTime.now();
threadLocalStart.set(startTime);
log.info("开始时间:{}", startTime.format(formatter));
return true;
}
// 记录结束时间
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
LocalTime endTime = LocalTime.now();
threadLocalEnd.set(endTime);
log.info("结束时间:{}", endTime.format(formatter));
}
// 计算接口执行时间
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
LocalTime startTime = threadLocalStart.get();
LocalTime endTime = threadLocalEnd.get();
log.info("接口执行时间:{} 毫秒", Duration.between(startTime, endTime).getNano() / 1000000);
}
}
然后将TimeInterceptor类配置到WebConfigurer类中,完成拦截器的注册。
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor).addPathPatterns("/*");
registry.addInterceptor(timeInterceptor);
}
}
启动工程并再次访问hello接口,我们会看到控制台输出如下日志: