基本概念
拦截器是Spring MVC提供的一个可以在客户端请求执行前,执行中,执行后进行相应逻辑处理的组件,类似于Servlet 中的过滤器Filter。
因为拦截器的特殊的执行时间,我们可以利用它做很多事,像我们常见的用户登入认证,权限拦截等都可以用拦截器实现
自定义拦截器
Spring MVC提供了一个拦截器接口,名字叫HandlerInterceptor,当我们需要使用拦截器时,只要实现该接口,重写里面的三个方法即可,如下面例子所示:
public class MySecondInterceptor implements HandlerInterceptor {
private long start_time;
private long end_time;
//处理器执行请求前
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
start_time=System.currentTimeMillis();
System.out.println("第二个拦截器请求结束前:第二个拦截器的方法已经执行");
return true;
}
//处理器执行请求结束后,视图层显示前(即控制器向前端发送页面前)
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("第二个拦截器请求结束后:请求结束后执行"+modelAndView);
System.out.println("返回页面:"+modelAndView.getViewName());
System.out.println("返回数据:"+modelAndView.getModel().get("key"));
//也可以在这里对Controller返回的结果进行操作
// modelAndView.setViewName("login");
}
//处理器完全执行请求后,即视图已经展示
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
end_time=System.currentTimeMillis();
System.out.println("第二个拦截器请求完全结束后:登入共耗时"+(end_time-start_time)+"毫秒");
}
}
参数说明:
request:请求处理接口
response:请求响应接口
handler:封装的是当前请求请求的控制器具体调用的方法和该方法的返回值类型
ex:异常
modelAndView:控制器返回的视图和数据信息
方法说明
preHandle:请求执行前,返回的是一个布尔值,为true继续执行(可能是继续执行下一个拦截器,如果没有拦截器了则继续执行当前请求),false请求中断,该方法通常用来执行权限拦截,登入认证等逻辑
postHandle:在DispartcherServlet(主控制器)调用Controller(控制器)后,在调用ViewResolver(视图解析器)返回给客户端页面前,需要注意的是该方法并不是一定会执行,它执行的前提必须满足以下两个条件
1)所有拦截器的preHandle方法返回true且不抛异常
2)所有拦截器的控制器Controller没有抛出异常
上述条件一个不满足该方法就不会执行,该方法通常用来执行controller组件中的共用逻辑,如日志记录等
afterCompletion:在DispartcherServlet将视图返回给前端之后执行,无论控制器有没有抛出异常都会执行,该方法通常配合preHandle实现对某些模块的性能监测,如监测用户登入的耗时等
执行顺序和配置
如果Spring MVC中配有多个拦截器,它们的方法执行是链式的,且postHandle和afterCompletion的顺序和preHandle是相反的。打个比方说,现在有三个拦截器A,B,C(顺序为拦截器的配置顺序),每个拦截器对应的三个方法分别是a1,a2,a3;b1,b2,b3,c1,c2,c3,那么执行顺序为
a1>b1>c1>c2>b2>a2>c3>b3>a3
如果b1返回false,则顺序为
a1>b1>c3
如果B拦截器的controller控制器抛异常的话则执行顺序为
a1>b1>c1>c3>b3>a3
可自行编写拦截器进行验证,不多赘述
操作控制器返回的数据
Spring MVC对于控制器的返回结果只有两种类型:String和ModelAndView,
如果控制器返回的是这两种类型的数据的话,我们可以很轻易的通过postHandle方法中的参数modelAndView在主控制器渲染视图和数据信息给客户端之前对这些数据进行操作。但是通常我们现在的一个web项目都是通过@ResponseBody注解给前端返回json类型的数据,那么此时我们会发现postHandel方法中的参数modelAndView无法接收controller返回的视图和数据信息,那么此时我们要怎么办呢?
定义一个类实现ResponseBodyAdvice,如下所示
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<JsonResult>{
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
//让请求继续执行,默认返回false,这里要让它返回true继续执行
return true;
}
public JsonResult beforeBodyWrite(JsonResult body,
MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
System.out.println("返回的json数据"+body.toString());
body.setMessage("修改返回的message数据");
return body;
}
}
需要注意的是该类一定要加上@ControllerAdvice注解,保证每一个@Controller执行后能执行这个类里面的方法,所以他的执行顺序在Controller之后,在postHandle之前。