HandlerInterceptor
The top-level interfaces of interceptors in Spring MVC are as follows:
package org.springframework.web.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.lang.Nullable; public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
Interceptor execution
stay Spring MVC process analysis At the end of the chapter, we know that the interceptor is encapsulated in the HandlerExecutionChain object and is a collection. That means we can have multiple interceptors working at the same time.
Let's take another look at the doService method of DispatcherServlet, which finally calls the doDispatch method of DispatcherServlet. The method is defined as follows:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); 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; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
It's too long. We truncate the key code as follows:
..... // mappedHandler is the HandlerExecutionChain object // This method is to obtain the corresponding HandlerAdapter through the handler object of the HandlerExecutionChain object HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); ...... // Call the applyPreHandle method of HandlerExecutionChain. If false is returned, return directly if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } ...... // Execute the handle method of the HandlerAdapter method (that is, execute the actual logic of our Controller) mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ...... // Perform some page rendering operations this.applyDefaultViewName(processedRequest, mv); // Call the applyPostHandle method of HandlerExecutionChain mappedHandler.applyPostHandle(processedRequest, response, mv); .......
There are only two methods related to our interceptor, applyPreHandle and applyPostHandle, which are both methods of the HandlerExecutionChain object
applyPreHandle method
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } } return true; }
Method directly calls the preHandle method of all configured interceptors. As long as the preHandle method of one interceptor returns false, it calls the triggerAfterCompletion method and returns false directly. Later interceptors are no longer executed.
For example, if the code applyPreHandle of DispatcherServlet returns false, the current request will return directly;
triggerAfterCompletion method
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = this.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 var8) { logger.error("HandlerInterceptor.afterCompletion threw exception", var8); } } } }
The triggerAfterCompletion method calls the afterCompletion methods of all interceptors in turn.
applyPostHandle method
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = this.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); } } }
The applyPostHandle method calls the postHandle methods of all interceptors in turn.
summary
- If an interceptor is added to our, the preHandle method will be executed before executing the request. If false is returned, the afterCompletion method of all interceptors will be executed, and then the request will be returned directly.
- The postHandle method of all interceptors is executed after the request is executed
- If there is an exception in the request process (see the DispatcherServlet source code), the triggerAfterCompletion method is called after the catch exception. This means that if an exception occurs, the interceptor's afterCompletion method is called.
Role of interceptor methods
preHandle method: executed before request processing. It can be used for permission authentication and other operations before request
postHandle method: executed after the request. It can be used to request logging and other operations after execution
afterCompletion method: it is mainly used to release the resources used by the current interceptor.
custom interceptor
Custom interceptors are generally implemented in the following ways
- Directly implement the HandlerInterceptor interface.
- Inherits the HandlerInterceptorAdapter abstract class
HandlerInterceptorAdapter is actually an adapter. After jdk1.8, the interface can define the default method, and the interface definition of HandlerInterceptor is an adapter by default. Therefore, we can override only one of the methods by directly implementing the HandlerInterceptor interface.
Inherit HandlerInterceptorAdapter to implement
package com.yyoo.springmvc.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Interceptor preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("Interceptor postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("Interceptor afterCompletion"); } }
Configuring Interceptors
Remember the configuration class that we implemented WebMvcConfigurer earlier? Rewrite the following code in this class to configure the interceptor.
@Bean public MyInterceptor myInterceptor(){ return new MyInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor()) .addPathPatterns("/**") // url of interceptor action .order(1);// Registration order of interceptors }
Previous: 009 spring MVC JSON returned
Next: to be continued