Spring Boot Practice: Creation of Three Interceptors

Posted by chriskl on Fri, 16 Aug 2019 09:19:42 +0200

Interceptors in Spring

In web development, interceptor is a commonly used function. It can help us verify whether to login, authorization, data validation, pre-set data and the efficiency of statistical methods, etc. Today I'll talk about the interceptor in spring in detail. There are two main types of interceptors in spring: Handler Interceptor and Method Interceptor.

Handler Interceptor Interceptor

Handler Interceptor is the interceptor in the spring MVC project. It intercepts the requested address, which is executed before Method Interceptor. Implementing a Handler Interceptor interceptor can directly implement the Handler Interceptor interface or inherit the Handler Interceptor Adapter class. The two approaches come together in the same way. In fact, the Handler Interceptor Adapter declares the default implementation of all the methods in the Handler Interceptor interface, and we only need to rewrite the necessary methods after inheriting them. Here is the code for Handler Interceptor Adapter. You can see that one method only returns true by default, and the other two are empty methods:

/** * Custom Interceptor-Based on Spring MVC
 * @ClassName: CustomInterceptor 
 * @Description: springMVC The interceptor in the project, which intercepts the address of the request, is executed before the Method Interceptor.
 *                 The interceptor can only filter action requests. SPring allows multiple interceptors to exist at the same time and is managed through the interceptor chain.
 *                 When preHandle return true, the next interceptor is executed until all interceptors are executed, and then the intercepted request is run.
 *                 When preHandle return false, subsequent interceptor chains and intercepted requests are no longer executed.
 * @author OnlyMate
 * @Date 2018 August 28, 2000, 2:30:22 p.m.  
 * */
public class CustomInterceptor implements HandlerInterceptor  {

    @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

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

}

What are the three methods, what are their functions, when to call them, and what is the calling order between different interceptors? This also refers to the doDispatch method of Dispatcher Servlet.

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

The code is a bit long, but it encapsulates the whole process of processing requests in spring MVC. First, the corresponding Handler Execution Chain is found according to the request, which contains the handler handling the request and all the Handler Interceptor interceptors; then the preHandle method of each Handler Interceptor interceptor is invoked separately before the hander is invoked, and if one interceptor returns false, the triggerAfterCompletion side is invoked. Method, and immediately return to no further execution; if all interceptors return true and no exception occurs, call handler to return the ModelAndView object; then call postHandle method of each interceptor separately; finally, triggerAfterCompletion side will be executed even if the previous step throws an exception Law. So far as the interceptor is concerned, let's look at what triggerAfterCompletion has done.

private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
            @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {

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

According to the above code, analyze the execution sequence of different interceptors and their methods. Assuming that there are five interceptors with 12345 numbers, if all normal methods are executed in the order of 12345 preHandle, 54321 postHandle and 54321 afterCompletion. If the preHandle method of interceptor No. 3 returns false or throws an exception, the next method to be executed is the afterCompletion method of 21. The point to note here is that when we write an interceptor, we should handle exceptions in preHandle with caution, because if there are exceptions thrown here, it will not be controlled by the interceptor. After the preHandle method of 12345 is executed, if the handler has an exception or the postHandle method of an interceptor has an exception, the afterCompletion method of 54321 will be executed next, because as long as the preHandle method of 12345 is executed, the interceptor of the current interceptor will be recorded as the interceptor of number 5, while the AF. TerCompletion is always performed backwards from the current interceptor.
Another way to implement the Handler Interceptor interceptor is to implement the WebRequest Interceptor interface. In fact, it is the same way as the previous two methods, and it is finally adapted to Handler Interceptor by spring. One difference is that its preHandle method eventually returns only true.

Here you can write your own business processing logic in the corresponding method according to your own needs.

/**
 * Custom Interceptor-Based on Spring MVC
 * @ClassName: CustomInterceptor 
 * @Description: springMVC The interceptor in the project, which intercepts the address of the request, is executed before the Method Interceptor.
 *                 The interceptor can only filter action requests. SPring allows multiple interceptors to exist at the same time and is managed through the interceptor chain.
 *                 When preHandle return true, the next interceptor is executed until all interceptors are executed, and then the intercepted request is run.
 *                 When preHandle return false, subsequent interceptor chains and intercepted requests are no longer executed.
 * @author OnlyMate
 * @Date 2018 August 28, 2000, 2:30:22 p.m.  
 *
 */
public class CustomInterceptor implements HandlerInterceptor  {
    private Logger logger = LoggerFactory.getLogger(CustomInterceptor.class);
    
    /**
     * Execution before request processing, mainly for permission validation, parameter filtering, etc.
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        logger.info("CustomInterceptor ==> preHandle method: do request before");
        return true;
    }

    /**
     * Current requests are processed and executed, mainly for logging, permission checking, performance monitoring, general behavior, etc.
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        logger.info("CustomInterceptor ==> postHandle method: do request after");
    }

    /**
     * When the return value of the current interceptor's perHandle method is true, the postHandle executes after completing and rendering the page, mainly for resource cleaning.
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        logger.info("CustomInterceptor ==> afterCompletion method: do request finshed");
    }
}

The configuration is as follows:

/**
 * Web MVC Configuration adapter
 * @ClassName: WebAppConfigurer 
 * @Description: 
 * @author OnlyMate
 * @Date 2018 28 August 2000, 2:39:31 p.m.  
 * 
 * WebAppConfigurer extends WebMvcConfigurerAdapter Spring Boot version 2.0 is out of date, replacing it with new classes described on the official website
 *
 */
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
    /**
     * Injection of custom interceptors
     * @Title: addInterceptors 
     * @Description: The closer the first add interceptor is, the closer it is to the browser
     * @Date 2018 28 August 2000, 2:47:28 p.m. 
     * @author OnlyMate
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        WebMvcConfigurer.super.addInterceptors(registry);
        registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");//Intercept all requests
    }

}

Method Interceptor Interceptor

MethodInterceptor is an interceptor in AOP projects. It intercepts methods, even if they are not methods in controller s. The implementation of MethodInterceptor interceptors can be roughly divided into two kinds, one is to implement the MethodInterceptor interface, and the other is to use AspectJ's annotations or configuration.

1. Implementing Method Interceptor Interface

/**
 * Custom interceptor-method interceptor, based on spring aop
 * @ClassName: CustomMethodInterceptor 
 * @Description: AOP The interceptor in the project, which intercepts the target is the method
 *                 Configured in applicationContext.xml
 * @author OnlyMate
 * @Date 2018 29 August 2000, 3:35:24 p.m.  
 *
 */
public class CustomMethodInterceptor implements MethodInterceptor {
    private Logger logger = LoggerFactory.getLogger(CustomMethodInterceptor.class);
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        logger.info("CustomMethodInterceptor ==> invoke method: process method name is {}", invocation.getMethod().getName());
        
        //TODO processing operation
        
        return invocation.proceed();
    }

}

Configuration instructions

<bean id="customMethodInterceptor" class="com.onlymate.springboot.interceptor.CustomMethodInterceptor" />
    
    <aop:config proxy-target-class="false">
        <! - Method interceptor, configuration based on spring aop - >
        <! - Scanning uses annotation to intercept
        <aop:advisor pointcut="@annotation(com.onlymate.springboot.annotation.CustomAnnotation)" advice-ref="customMethodInterceptor" />
        <! - Specify the method under the package path - >
        <aop:advisor pointcut="execution(* com.onlymate.springboot.controller.*.*(..))" advice-ref="customMethodInterceptor" />
    </aop:config>

Custom Annotation Custom Annotation Custom Annotation

/**
 * Custom Annotation Object
 * @ClassName: TableSplit 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018 22 May 2000, 11:43:57 a.m.  
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
    /** Name description of interception method */
    String name() default "";
    
    /** encryption */
    String[] encrypt() default {};

    /** Decrypt */
    String[] decrypt() default {};

}

2. Annotation or configuration using AspectJ

a. Based on AspectJ Annotation

/**
 * Custom Interceptor-Method Interceptor, Annotation-Based AspectJ Approach
 * @ClassName: CustomAutoAspectJInterceptor 
 * @Description: Configured in applicationContext.xml
 * @author OnlyMate
 * @Date 2018 29 August 2000, 4:03:49 p.m.  
 *
 */
@Component
@Aspect
public class CustomAutoAspectJInterceptor {
    private Logger logger = LoggerFactory.getLogger(CustomAutoAspectJInterceptor.class);
    
    @Around("execution (* com.onlymate.springboot.controller.*.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        logger.info("CustomAutoAspectJInterceptor ==> invoke method: process method class is {}", point.getTarget().getClass());
        
        //TODO processing operation
        
        return point.proceed();
    }
}

b. Based on AspectJ configuration

/**
 * Custom Interceptor-Method Interceptor Based on AspectJ
 * @ClassName: CustomAspectJInterceptor 
 * @Description: Configured in applicationContext.xml
 * @author OnlyMate
 * @Date 2018 29 August 2000, 4:03:49 p.m.  
 *
 */
public class CustomAspectJInterceptor {
    private Logger logger = LoggerFactory.getLogger(CustomAspectJInterceptor.class);
    
    public Object around(ProceedingJoinPoint point) throws Throwable{
        logger.info("CustomAspectJInterceptor ==> invoke method: process method class is {}", point.getTarget().getClass());
        
        //TODO processing operation
        
        return point.proceed();
    }
}

c. Configuration instructions

<bean id="customAspectJInterceptor" class="com.onlymate.springboot.interceptor.CustomAspectJInterceptor"/>
    <aop:config proxy-target-class="false">
        Method Interceptor Based on AspectJ Implementation Mode 1 - > Method Interceptor
        <aop:aspect ref="customAspectJInterceptor">
            <aop:around method="around" pointcut="execution(* com.onlymate.springboot.controller.*.*(..))"/>
        </aop:aspect>
        
    </aop:config>

    Method Interceptor Based on AspectJ Implementation Mode 2
    <! -- Classes that use aspectj annotations for automatic scanning - >
    <aop:aspectj-autoproxy/>

III. EFFECT CHARACTERISTICS

Fourth, Talk about the Difference

Both of the above interceptors can intercept, but they intercept different targets and achieve different mechanisms, so sometimes different scenarios are applicable. Handler Interceptoer intercepts the request address, so it is appropriate to do some validation, preprocessing and other operations for the request address. When you need to count the response time of a request, the Method Interceptor will not be easy to do, because it may span many methods or involve only part of the code in a defined method. MethodInterceptor uses the implementation mechanism of AOP. In this paper, only the way of using AOP is explained. There is less introduction about the principle and mechanism, because it needs to explain a considerable part of AOP. There's nothing we can do to intercept Handler Interceptoer on some common methods, so we can only use AOP's Method Interceptor. Using Method Interceptor, it is easy to implement a log interception process.

   In addition, there is something similar to the interceptor - Filter. Filter is defined by the Servlet specification, which does not belong to the spring framework and is also used to intercept requests. However, it is suitable for more coarse-grained interception, some coding and decoding processing, logging and so on before and after the request. Interceptors can provide finer-grained, more flexible solutions for combinations of certain requests and methods.

Topics: Java Spring SpringBoot xml Web Development