拦截器 Interceptor
实现一个自己拦截器只需要两个步骤即可:
- 新建一个类实现HandlerInterceptor接口;
重写我们需要的方法,如上的三个方法并不是必须的,视自己的业务需求而定。
public class MyInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);
* false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("preHandle");
// 返回false会阻止调用链的继续进行
return true;
}
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("postHandle");
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finall
* 但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("afterCompletion");
}
}
- 在实现了自己的拦截器后还不能马上使用,还需要在Spring中注册与配置,我们新建一个配置类,将上面我们实现的拦截器进行注册,此时拦截器就能生效了。
@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
多拦截器的处理过程
当我们需要多个拦截器的时候,每个拦截器的方法处理顺序如下:
Interceptor1:preHandle > Interceptor2:preHandle > Interceptor2:postHandle > Interceptor1:postHandle > Interceptor2:afterCompletion > Interceptor1: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");
}
}
拦截器适配器HandlerInterceptorAdapter
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor接口的话,三个方法必须实现,不管你需不需要,此时spring提供了一个HandlerInterceptorAdapter适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法。