基于HandlerInterceptor的拦截器使用及实现
拦截器的作用,顾名思义,就是对请求进行拦截,做一些额外的处理,在Spring5.1.2中想要实现一个拦截器就只有通过实现HandlerInterceptor
接口,重写preHandle
、postHandle
、afterCompletion
方法来完成,整个过程都相对简洁易懂。
一、实现自己的拦截器
public class MyInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("preHandle");
// 返回false会阻止调用链的继续进行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("afterCompletion");
}
}
实现一个自己拦截器只需要两个步骤即可:
- 新建一个类实现
HandlerInterceptor
接口; - 重写我们需要的方法,如上的三个方法并不是必须的,视自己的业务需求而定。
在实现了自己的拦截器后还不能马上使用,还需要在Spring中注册与配置,我们新建一个配置类,将上面我们实现的拦截器进行注册,此时拦截器就能生效了。
@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
启动程序后,我们访问任意的一个Controller就会在控制台看到拦截器中打印的信息:
preHandle
postHandle
afterCompletion
PS :本实验是在SpringBoot中进行的,真的感叹太方便了。
二、多拦截器的处理过程
当我们需要多个拦截器的时候,每个拦截器的方法处理顺序如下:
Interceptor1:preHandle > Interceptor2:preHandle > Interceptor2:postHandle > Interceptor1:postHandle > Interceptor2:afterCompletion > Interceptor1:afterCompletion
我们不妨来写个例子实践一下:
public class MyInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("MyInterceptor:preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("MyInterceptor:postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("MyInterceptor:afterCompletion");
}
}
public class YourInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(YourInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("YourInterceptor:preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("YourInterceptor:postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("YourInterceptor:afterCompletion");
}
}
@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
registry.addInterceptor(new YourInterceptor());
}
}
我们在拦截器配置类InterceptorConfig
中添加的顺序就是拦截器执行的顺序,在如上的例子中,MyInterceptor
排在YourInterceptor
前面,其拦截的结果如下:
MyInterceptor:preHandle
YourInterceptor:preHandle
YourInterceptor:postHandle
MyInterceptor:postHandle
YourInterceptor:afterCompletion
MyInterceptor:afterCompletion
为什么会有这样的执行结果呢?其实这和HandlerInterceptor
中三个方法的执行时机有关系:
preHandle:在Controller处理之前做拦截进行处理,因此越是声明在前的拦截器,其
preHandle
方法就越先得到执行。-
postHandle:在Controller处理之后,视图渲染返回之前进行处理,有点像括号嵌套的机制,左括号越是在前,其对应的右括号越是在后。
(PS:这里的“返回之前”只是针对视图渲染逻辑的处理,如果是Rest的接口,那么调用会在Controller处理之后直接返回,不会等到postHandle处理完之后才返回)
afterCompletion:在所有处理完成后用于对资源进行回收进行处理,因此,所有的
afterCompletion
都会在所有的preHandle
和postHandle
之后才会执行,且和postHandle
一样,按照右括号规则执行。
三、对特定请求地址的拦截
我们可以在拦截器中定义对那些URL需要拦截,而哪些URL是不需要做任何处理的。
@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 对所有的请求都要拦截,但是“/getInt”不在拦截的范围之内
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**").excludePathPatterns("/getInt");
}
}
全文完。