spring mvc interceptor的资源清理

今天在增加基于模板名称的interceptor的时候,突然想到如果有多个interceptor依次,那么会不会影响某个interceptor的资源清理工作?因为资源清理一般是在afterCompletion方法中执行的,但在afterCompletion方法的文档上写道:

Note: Will only be called if this interceptor’s preHandle method has successfully completed and returned true!

意思就是这个拦截器的afterCompletion方法只在preHandle方法完全执行并且返回true之后才会被调用,那么就会有这样的疑问,拦截器A的preHandle方法设置了一些上下文后返回true,并且在completion中执行清理:

public class AInterceptor extends HandlerInterceptorAdapter{

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		UserContext.set(user);
		return true;
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		UserContext.remove();
	}

}

接下来拦截器B执行,preHandle直接返回false:

public class BInterceptor extends HandlerInterceptorAdapter{

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return false;
	}
}

那么此时拦截器A的afterCompletion方法还会不会执行呢?刚开始我认为由于拦截器B的原因A的清理方法不会执行了,但是看了源码之后发现担心完全是多余的,拦截器的几个方法是通过HandlerExecutionChain这个对象来执行的(可以看下DispatcherServlet的 doDispatch方法),而HandlerExecutionChain的执行preHandle方法如下:

	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;
	}

这里可以清晰的看到,当某个拦截器的preHandle方法返回false之后,会调用所有被执行的拦截器的afterCompletion方法,因此,上面的拦截器A的afterCompletion方法依旧能被执行。

2018.1.5

那么如果B拦截器在preHandler方法中抛出了异常,那么A和B两个拦截器的afterCompletion执行又会如何呢?
首先只看上面给出的代码,当B的preHandler方法抛出异常之后,triggerAfterCompletion是不会被执行的,也就是说,此时A和B的afterCompletion都没有被执行,但回到DispatherServlet的doDispatch方法,可以看到,spring吃掉了抛出的异常:

    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
...
} catch (Exception ex) {
    dispatchException = ex;
} catch (Throwable err) {
    // As of 4.3, we're processing Errors thrown from handler methods as well,
    // making them available for @ExceptionHandler methods and other scenarios.
    dispatchException = new NestedServletException("Handler dispatch failed", err);
}

最终会执行processDispatchResult这个方法,这个方法会再次调用triggerAfterCompletion方法,

if (mappedHandler != null) {
 mappedHandler.triggerAfterCompletion(request, response, null);
}

因此A的afterCompletion仍然会执行,但B的不会,事实上,哪怕processDispatchResult这个方法抛出异常,triggerAfterCompletion还是会被执行

所以在某个拦截器产生的资源可以放心的由该拦截器进行清理