一、拦截器
SpringMVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor
接口
preHandle
目标方法运行之前调用
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
postHandle
目标方法运行之后调用,目标方法一调用就会执行
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
afterCompletion
在请求整个完成之后,来到目标页面之后,资源响应之后才执行
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
二、单拦截器运行流程
请求处理目标方法
@Controller
public class InterceptorTestController {
@RequestMapping("/testInterceptor")
public String testInterceptor(){
System.out.println("执行目标方法");
return "success";
}
}
创建一个拦截器类实现HandlerInterceptor
接口
public class MyFirstInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyFirstInterceptor...preHandle()执行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstInterceptor...postHandle()执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyFirstInterceptor...afterCompletion()执行");
}
}
在SpringMVC配置文件中注册这个拦截器的工作,配置这个拦截器来拦截哪些请求的目标方法
<!--配置拦截器-->
<mvc:interceptors>
<!--配置某个拦截器,默认拦截所有请求-->
<bean class="controller.MyFirstInterceptor"></bean>
<!--
<!–配置某个拦截器更详细的信息–>
<mvc:interceptor>
<!–只拦截指定的请求–>
<mvc:mapping path="/testInterceptor"/>
<bean class="controller.MyFirstInterceptor"></bean>
</mvc:interceptor>
-->
</mvc:interceptors>
执行流程的打印结果:
正常流程:拦截器的preHandler
—>目标方法—>拦截器的postHandler
—>页面—>拦截器的afterCompletion
注意,如果preHandler
不放行,就没有之后的流程。但是如果执行目标方法执行了,不管报没报错,afterCompletion
都会执行,而如果报错,postHandler
不会执行。
三、多拦截器的运行流程
配置多个拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置某个拦截器,默认拦截所有请求-->
<bean class="controller.MyFirstInterceptor"></bean>
<!--配置某个拦截器更详细的信息-->
<mvc:interceptor>
<!--只拦截指定的请求-->
<mvc:mapping path="/testInterceptor"/>
<bean class="controller.MySecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
正常流程,多个拦截器同时工作,执行流程如下:
如果出现异常,只要其中任何一个环节出现异常,后续的方法都不会执行。
但是如果MySecondInterceptor
的preHandler
出现异常,执行流程如下:
即使MySecondInterceptor
的preHandler
不放行,MyFirstInterceptor
还是会执行它的afterCompletion
。也就是MySecondInterceptor
之前已经放行了的其它拦截器还是会执行各自的afterCompletion
四、拦截器执行流程源码分析
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
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(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
在mappedHandler = getHandler(processedRequest)
方法执行后,mappedHandler
获取的是HandlerExecutionChain
,也就是说获取的不只有目标方法,还有所有的拦截器。
applyPreHandler()
先是执行所有拦截器的preHandler
,循环遍历每一个拦截器,如果preHandler
不放行,就会触发执行AfterCompletion()
方法,然后再返回false
;如果preHandler
放行,就会用索引记录一下已放行的拦截器。只要有一个拦截器返回false,目标方法以后的方法都不会执行,直接跳到afterCompletion()
方法。
注意:interceptorIndex
索引值初始化为-1
,因为在triggerAfterCompletion()
方法中,会倒序遍历每个拦截器的afterCompletion()
方法。所以如果不是初始化为-1
,在没有执行preHandler()
方法的情况下,也会执行一遍多余的afterCompletion()
方法。
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;
}
然后执行ha.handle()
方法,适配器执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
如果目标方法正常执行,就会执行applyPostHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
applyPostHandle()
的循环遍历是倒序遍历
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
}
}
}
无论目标方法是否正常执行都将会执行页面渲染方法processDispatchResult()
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
如果render(mv, request, response)
页面渲染正常执行完成,渲染完成以后,就会执行afterCompletion()
方法。
如果页面渲染执行也有异常,doDispatch()
方法中会捕捉到异常,异常处理也会执行afterCompletion()
方法
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
所以afterCompletion()
方法不管怎样都会执行。
triggerAfterCompletion()
方法能获取到已放行拦截器的所有索引,通过倒序遍历执行所有已放行拦截器的afterCompletion()
方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
}
}
}
}
综上所述:
单拦截器执行顺序
多拦截器执行顺序
五、Filter过滤器与拦截器
对于拦截器,它是放在容器中的,需要配置。如果需要某些功能,可以装配其它组件配合完成。如果过滤请求处理非常复杂,就可以使用拦截器。拦截器脱离了SpringMVC就没有用了。
对于Filter过滤器,它是JavaWeb的三大组件,但是不能像拦截器一样自动装配一些其它组件,Filter是交给Tomcat管理的,服务器一启动就会创建Filter对象,但是它不能加载到IOC容器中,也就无法从容器中获取其它组件。如果过滤请求处理比较简单,就可以使用Filter。Filter脱离SpringMVC还是能用。