SpringMVC中Interceptor拦截器实现原理分析

SpringMVC中Interceptor 拦截器讲解:

  1. 在SpringMVC中实现拦截器需要实现 HandlerInterceptor,其中包含三个需要的方法:preHandle()、postHandle()、afterCompletion()
  2. preHandle(): 在Controller method 之前执行,通常用于做参数校验、登录验证等。
  3. postHandle(): 在Controller method 之后执行,需要注意的是,当Controller中发生异常时,并不会执行postHandle,而是跳过后执行afterCompletion 方法。
  4. afterCompletion(): 在postHandle执行之后执行,发生异常也会执行,通常用于释放系统资源。
  5. HandlerInterceptor 代码示例 :
public interface HandlerInterceptor {

        /**
         * 在Controller method 之前执行,通常用于做参数校验、登录验证等
         * 参数handler requestMapping 修饰的 Controller方法
         */
        default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception{
            return true;
        }

        /**
         *  Controller method 之后执行,需要注意的是,当Controller中发生异常时,并不会执行
         */
        default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                                @Nullable ModelAndView modelAndView) throws Exception {
        }

        /**
         * 在postHandle执行之后执行,发生异常也会执行,通常用语释放系统资源
         */
        default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                     @Nullable Exception ex) throws Exception {
        }
    }

论证上述关于Interceptor 三个方法执行的顺序:

  1. 首先需要搭建一个SpringMVC 项目,这里就不过多解释了。
  2. 新建一个拦截器 SimpleHandlerInterceptor,代码示例如下:
public class SimpleHandlerInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        System.err.println("SimpleHandlerInterceptor.preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.err.println("SimpleHandlerInterceptor.postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.err.println("SimpleHandlerInterceptor.afterCompletion");
    }
}
  1. 在SpringMVC 的配置文件加入下面拦截器配置:
<mvc:interceptors>
        <bean class="com.springmvc.config.SimpleHandlerInterceptor" />
    </mvc:interceptors>
  1. 新建一个SimpleController 用于调用:
@RestController
@RequestMapping("/simple")
public class SimpleController {

	// 正常情况
    @RequestMapping("/test")
    public void testVoid(){
        System.err.println(" 进入SimpleController.testStr 方法");
    }
	
	// 发生异常时
    @RequestMapping("/exception")
    public void testException(){
        System.err.println(" 进入SimpleController.testException 方法");
        // 模拟系统异常
        int i = 1/0;
    }

}
  1. 发送请求:
    正常请求: http://localhost:8080/simple/test, 打印:
    在这里插入图片描述
    异常请求: http://localhost:8080/simple/exception, 打印:
    在这里插入图片描述
  2. 上面打印结果已经论证了我们的想法,那为什么会有这样的执行结果那?

源码浅析:

  1. 众所周知,SpringMVC和核心组件是 DispatcherServlet 所有经Sringmvc的请求,都会执行doDispatch 方法,其实拦截器执行便是在其中设定的。
  2. 题外话: SpringMVC 和 Mybatis 相对而言是比较容易去阅读源码的框,它们都有统一的入口,一步步Debug 也可以看懂其中核心的执行流程,不过废话,执行源码搞起:
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);

                // 获取RequestMapping修饰的Controller method 【包含调用链】
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 获取可以执行当前Handler的适配器
                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;
                    }
                }
                // 注意: 执行Interceptor中PreHandle()方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 注意:执行Handle【包括我们的业务逻辑,当抛出异常时会被Try、catch到】
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                applyDefaultViewName(processedRequest, mv);

                // 注意:执行Interceptor中PostHandle 方法【抛出异常时无法执行】
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 注意:其中会执行执行Interceptor中afterCompletion 方法【没有异常时】
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            // 注意:其中会执行执行Interceptor中afterCompletion 方法【没有异常时】
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            // 注意:其中会执行执行Interceptor中afterCompletion 方法【没有异常时】
            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);
                }
            }
        }
    }
  1. doDispatch中关于 Interceptor 执行顺序已经很清楚了,需要注意的是PostHandle 执行情况,以及afterCompletion 会在三种情况下被执行

以上仅是博主对 SpringMVC中Interceptor拦截器的简单理解,欢迎大家评论

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值