There are two main types of interceptors in SpringBoot: HandlerInterceptor and MethodInterceptor.
An overview of the
HandlerInterceptor
The following shows how to implement a global interceptor based on implementing the HandlerInterceptor interface
/** * CustomInterceptor - based on springmvc * @classname: CustomInterceptor * @description: The interceptor in the springMVC project, which intercepts the requested address before the MethodInterceptor. * This interceptor can only filter action requests, and SPring allows multiple interceptors to exist at the same time, managed through a chain of interceptors. * When preHandle Return true, the next interceptor is executed and the intercepted request is not run until all interceptors have been executed. * When preHandle Return false, subsequent interceptor chains and intercepted requests are not executed. * @author OnlyMate * @date 2018年8月28日 2:30pm * */ Public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); if (Objects.nonNull(method.getAnnotation(NoAuthenticate.class)) || Objects.nonnull (method.getDeclaringClass().getannotation (noauthenticate.class)) {return true; } String accessToken = request.getHeader("accesstoken"); log.info("------------accessToken:"+accessToken); log.info("------------redisHost:"+redisHost+"---redisPort:"+redisPort); If (accessToken = = null | | accessToken. IsEmpty ()) {throw new BaseException (100011, "please visit!" ); } SessionHelper.setAccessToken(accessToken); String str= (String) redisTemplate.opsForValue().get(accessToken); If (STR == null) {throw new BaseException(100011, "please login! ); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub HandlerInterceptor.super.afterCompletion(request, response, handler, ex); }}Copy the code
What do these three methods do, what do they do, when are they called, and in what order are they called from interceptor to interceptor? Also refer to the doDispatch method of DispatcherServlet
The following code encapsulates how SpringMVC handles the request. Find the corresponding HandlerExecutionChain based on the request. It contains the handler that handles the request and all HandlerInterceptor interceptors. It then calls the preHandle method of each HandlerInterceptor interceptor before calling the Hander. If one of the interceptors returns false, the triggerAfterCompletion method is called and immediately returns no further execution. If all interceptors return true and no exception occurs, handler is called to return the ModelAndView object; Then call each interceptor’s postHandle method separately; Finally, the triggerAfterCompletion method is executed even if the previous step threw an exception.
Take a look at the sequence of execution of the different interceptors and their methods based on the above code. Assume that there are five interceptors numbered 12345, and if all goes well, the method will be executed in the order of 12345 preHandle, 54321 postHandle, and 54321 afterCompletion. If the preHandle method of the interceptor number 3 returns false or throws an exception, afterCompletion method 21 will follow. The important thing to note here is that we should be careful to handle exceptions in the preHandle when writing an interceptor, because any exceptions thrown here are no longer under the control of the interceptor. After the 12345 preHandle method is executed, if there is an exception in the handler or in one of the interceptor’s postHandle methods, 54321 afterCompletion will be executed as soon as the 12345 preHandle method is completed. The current interceptor’s interceptor is logged as interceptor number 5, and afterCompletion always executes backwards from the current interceptor.
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 (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } 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); }}}}Copy the code
Finally, the implements HandlerIntercetor interceptor interceptor also needs to be added to the SpringBoot implementation of WebMvcCongigurer’s configuration class. Here’s how it works. In addition, the more the Add interceptor is pushed out, the closer it is to the browser
@configuration Public class AppConfig implements WebMvcConfigurer {// Implements the @autoWired interceptor of the implements HandlerIntercetor interface SessionInterceptor sessionInterceptor; @override public void addInterceptors(InterceptorRegistry) {Override public void addInterceptors(InterceptorRegistry) { registry.addInterceptor(sessionInterceptor) .addPathPatterns("/**") .excludePathPatterns("/swagger-ui.html", "/v2/api-docs/**", "/swagger-resources/**", "/webjars/**", "/api/v1/passwordforget"); } public void addCorsMappings(CorsRegistry registry) {registry. AddMapping ("/**"). AllowedOrigins ("*") .allowedMethods("*"); }}Copy the code
MethodInterceptor
A MethodInterceptor is an interceptor in an AOP project that intercepts methods, even if they are not methods in a Controller. There are two main types of interceptors: those that implement the MethodInterceptor interface, and those that make use of AspectJ annotations or configurations. (This is only for the @AspectJ annotation implementation.)
Pointcuts are introduced first: pointcuts can be made through custom annotations (which we won’t talk about here) or through pointcut expressions (excution), which we’ll cover next
For example, define the pointcut expression execution (*
com.sample.service.impl.. *. * (..) Execution () is the most common form of an expression, and its syntax is as follows: 2. The first * indicates the return type, and the * indicates all types. 3. Package name: indicates the name of the package to be intercepted. The following two periods indicate the current package and all subpackages of the current package, com.sample.service. 4. The second * indicates the name of the class, and the * indicates all classes. Here.. * denotes all classes in any recursion directory under the IMPl package.* denotes all classes under the IMPl package. The final asterisk represents the method name, the asterisk represents all methods, the parenthesis represents method arguments, and the two periods represent any argumentsCopy the code
Here are a few comments about the AOP approach
@before (Before method execution) @after (After method execution) @around (Before pointcut content and can control whether the content of the pointcut itself is executed, It is also possible to cut content after a pointcut.) @afterreturning (cutting content after a pointcut return, usually used to process returned values) @Afterthrowing (handling logic after a pointcut throws an exception)
The following is sample code: Intercepting a Session an API can only be executed once at a specified time
@slf4j //log @aspect //Aop @Component // Let the interceptor inject into SpringBoot public Class NoRepeatSubmitAop {@autoWired private RedisTemplate redisTemplate; / / intercept all with @ NoRepeatSubmit the @pointcut annotation method (" @ the annotation (cn. Jwis. Digitaldevicemate. Base. Annotations. NoRepeatSubmit) ") to the public Void pointCut() {} @annotation(XXX) specifies the name of the pointer. @around ("pointCut() && @annotation(noRepeatSubmit)") public Object Arround (ProceedingJoinPoint point, NoRepeatSubmit noRepeatSubmit) throws Throwable{ try { int i = noRepeatSubmit.repeatTimeOut(); ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder .getRequestAttributes(); String sessionId = RequestContextHolder.getRequestAttributes().getSessionId(); String servletPath = attributes.getRequest().getServletPath(); String key = sessionId + "-" +servletPath; If (redistemplate.haskey (key)) {return resultInfo.fail (" resubmit "); } else {/ / cut method continue redisTemplate opsForValue (). The set (key, 0, noRepeatSubmit repeatTimeOut (), TimeUnit. MILLISECONDS); Object o = point.proceed(); return o; } } catch (Throwable e) { return ResultInfo.Fail(e.getMessage()); }}}Copy the code
Execution time
The execution timing of the two is shown below: