转载自:
https://blog.csdn.net/jack__frost/article/details/71158139
https://blog.csdn.net/qq924862077/article/details/53541678
Spring MVC允许通过处理拦截拦截web请求,进行前置处理和后置处理。处理拦截是在Spring的web应用程序上下文中配置的,因此它们可以利用各种容器特性,并引用容器中声明的任何Bean。处理拦截是针对特殊的处理程序映射进行注册的,因此它只拦截通过这些处理程序映射的请求。
HandlerInterceptor类似于Servlet开发中的过滤器Filter,用于处理器进行预处理和后处理。主要作用是拦截用户的请求并进行相应的处理,其他的作用比如通过它来进行权限验证,或者是来判断用户是否登陆,日志记录,或者限制时间点访问。
除了这个之外,还可以用Spring的WebRequestInterceptor接口或者是继承实现了WebRequestInterceptor的类。
HandlerInterceptor接口
public interface HandlerInterceptor {
// 在请求处理之前进行调用
// 只有返回true,才会继续执行后续的Interceptor和Controller
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
// 在当前请求进行处理之后,也就是Controller方法调用之后执行
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
// 该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
preHandle:
SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。
postHandle:
只能是在当前所属的Interceptor的preHandle方法的返回值为true时才能被调用。Controller方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以可以在这个方法中对Controller 处理之后的ModelAndView对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor的postHandle 方法反而会后执行。
afterCompletion:
该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。系统日志的拦截可以在这个方法中,记录日志的相关的参数,检测方法的执行。
第一个和第二个方法分别是在处理程序处理请求之前和之后被调用的。第二个方法还允许访问返回的ModelAndView对象,因此可以在它里面操作模型属性。最后一个方法是在所有请求处理完成之后被调用的(如视图呈现之后).
示例:
public class HandlerInterceptorImpl1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("---------HandlerInterceptorImpl1 preHandle----------");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("---------HandlerInterceptorImpl1 postHandle----------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("---------HandlerInterceptorImpl1 afterCompletion----------");
}
}
public class HandlerInterceptorImpl2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("---------HandlerInterceptorImpl2 preHandle----------");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("---------HandlerInterceptorImpl2 postHandle----------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("---------HandlerInterceptorImpl2 afterCompletion----------");
}
}
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
HandlerInterceptorImpl1 impl1 = new HandlerInterceptorImpl1();
HandlerInterceptorImpl2 impl2 = new HandlerInterceptorImpl2();
registry.addInterceptor(impl1)
.excludePathPatterns("/error") // 排除配置
.addPathPatterns("/*"); // 拦截配置
registry.addInterceptor(impl2)
.addPathPatterns("/*"); // 拦截配置
}
}
运行结果:
---------HandlerInterceptorImpl1 preHandle----------
---------HandlerInterceptorImpl2 preHandle----------
---------HandlerInterceptorImpl2 postHandle----------
---------HandlerInterceptorImpl1 postHandle----------
---------HandlerInterceptorImpl2 afterCompletion----------
---------HandlerInterceptorImpl1 afterCompletion----------
除了实现HandlerInterceptor外,还可以使用继承HandlerInterceptorAdapter来达到目的。
这里的拦截器的顺序跟代码里的注册顺序是一致。
格外注意这里的每一个方法的调用顺序,A-pre、B-pre、B-post、A-post、B-after、A-after。
只有经过DispatcherServlet 的请求,才会走拦截器链,我们自定义的Servlet 请求是不会被拦截的,
与Filter区别:
1.filter基于filter接口中的doFilter回调函数,interceptor则基于Java本身的反射机制;比如HandlerInterceptor是交给spring bean工厂去反射生成的。
2.filter是依赖于servlet容器的,没有servlet容器就无法回调doFilter方法,而interceptor与servlet无关;
3.filter的过滤范围比interceptor大,filter除了过滤请求外通过通配符可以保护页面、图片、文件等,而interceptor只能过滤请求,只对controller起作用,在controller之前开始,在controller完成后结束(如被拦截,不执行action);
HandlerInterceptor应用场景
1、日志记录,可以记录请求信息的日志,以便进行信息监控、信息统计等。
2、权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
3、性能监控:典型的是慢日志。
补充:
来分析一下HandlerInterceptor
的三个方法具体调用实现的地方。其最终实现调用的地方是在DispatcherServlet#doDispatch
函数中,因为doDispatch
完成了一个请求到返回数据的完整操作。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerExecutionChain mappedHandler = null;
//...
try {
//...
try {
//...
// 获取HandlerExecutionChain
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
//...
// 最终会调用HandlerInterceptor的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用具体的Controller中的处理方法
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//...
// 最终会调用HandlerInterceptor的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
//...
}
//...
}
catch (Exception ex) {
//最终会调用HandlerInterceptor的afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//最终会调用HandlerInterceptor的afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
//...
}
}
mappedHandler.applyPreHandle()
最终会调用HandlerInterceptor
的preHandle
方法。在HandlerExecutionChain
中的具体实现如下,我们可以看到会调用所有的HandlerInterceptor
拦截器并调用其preHandler
方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
mappedHandler.applyPostHandle()
最终会调用HandlerInterceptor
的postHandle
方法:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
triggerAfterCompletion()
和triggerAfterCompletionWithError()
最终会调用HandlerInterceptor
的afterCompletion
方法:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}