• springMVC 拦截器源码解析


    前言:这两天学习了代理模式,自然想到了 springmvc 的 aop 使用的就是动态代理,拦截器使用的就是 jdk 的动态代理。今天看了看源码,记录一下。转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9969360.html

    springMVC 的拦截器使用移步:Java Servlet 过滤器与 springmvc 拦截器的区别?

    源码解析

      springMVC 的所有连接入口都会进入 DispatcherServlet,然后在这里面去调用真正的 Controller。而拦截器要达到的作用则是在调用 Controller 前后去做一些事情。所以现在需要看看 DispatcherServlet 的源码。

    DispatcherServlet 

      DispatcherServlet 的源码入口在 doService() 方法。

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            if(this.logger.isDebugEnabled()) {
                String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";
                this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
            }
    
            Map<String, Object> attributesSnapshot = null;
            if(WebUtils.isIncludeRequest(request)) {
                attributesSnapshot = new HashMap();
                Enumeration attrNames = request.getAttributeNames();
    
                label108:
                while(true) {
                    String attrName;
                    do {
                        if(!attrNames.hasMoreElements()) {
                            break label108;
                        }
    
                        attrName = (String)attrNames.nextElement();
                    } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
    
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
    
            request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
            request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
            request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
            request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if(inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
    
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    
            try {
                this.doDispatch(request, response);
            } finally {
                if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                    this.restoreAttributesAfterInclude(request, attributesSnapshot);
                }
    
            }
    
        }

      doService() 方法里面处理了一些信息之后,调用了 doDispatch() 方法。

        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 || mappedHandler.getHandler() == 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(this.logger.isDebugEnabled()) {
                                this.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;
                        }
    
                        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);
                }
    
            }
        }

      doDispatch() 方法中通过 this.getHandler(processedRequest); 获取到了处理器链 HandlerExecutionChain mappedHandle 之后,去执行了  mappedHandler.applyPreHandle(processedRequest, response) 方法,从而执行了拦截器的 preHandle 方法,如果返回为 false,则会调用处理器链的 triggerAfterCompletion 方法,然后 DispatchServlet 类中会直接 retreturn;如果返回为 true,则继续。

      然后调用了 HandlerAdapter 的 handler() 方法;

      然后调用了 mappedHandler.applyPostHandle(processedRequest, response, mv); 方法,从而执行了拦截器的 postHandle 方法;

      然后调用了 this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); 方法;在 processDispatchResult() 方法里面渲染了对应的视图,然后调用了 mappedHandler.triggerAfterCompletion(request, response, (Exception)null); 方法,从而执行了拦截器的 afterCompletion 方法。

      processDispatchResult() 源码如下:

        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
            boolean errorView = false;
            if(exception != null) {
                if(exception instanceof ModelAndViewDefiningException) {
                    this.logger.debug("ModelAndViewDefiningException encountered", exception);
                    mv = ((ModelAndViewDefiningException)exception).getModelAndView();
                } else {
                    Object handler = mappedHandler != null?mappedHandler.getHandler():null;
                    mv = this.processHandlerException(request, response, handler, exception);
                    errorView = mv != null;
                }
            }
    
            if(mv != null && !mv.wasCleared()) {
                this.render(mv, request, response);
                if(errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            } else if(this.logger.isDebugEnabled()) {
                this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
            }
    
            if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                if(mappedHandler != null) {
                    mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
                }
    
            }
        }

      从 doDispatch() 方法这里主要需要知道 HandlerExecutionChain mappedHandler = this.getHandler(processedRequest); 这一句调用的 getHandler() 方法,源码如下。

      getHandle() 方法里是通过 HandlerMapping 的 getHandler 方法返回 HandlerExecutionChain 的。

      从代码中不难看出整个逻辑就是依次判断 servlet 中的每个 handlerMapping 是否能够匹配该请求,直到找到那个匹配的然后返回处理结果。

        protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            Iterator var2 = this.handlerMappings.iterator();
    
            HandlerExecutionChain handler;
            do {
                if(!var2.hasNext()) {
                    return null;
                }
    
                HandlerMapping hm = (HandlerMapping)var2.next();
                if(this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
                }
    
                handler = hm.getHandler(request);
            } while(handler == null);
    
            return handler;
        }

    HandlerExecutionChain 类

      applyPreHandle() 方法源码如下:可以看出是顺序调用拦截器的 preHandle() 方法。preHandle 返回 false 时调用了 triggerAfterCompletion 方法。

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

      applyPostHandle() 方法源码如下:可以看出这里是逆序调用 postHandle 方法。

        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, 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);
                }
            }
    
        }

     triggerAfterCompletion() 方法源码如下:可以看出这里也是逆序调用 afterCompletion 方法。

        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, 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);
                    }
                }
            }
    
        }

       

    转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9969360.html

  • 相关阅读:
    request.json 打印中文乱码解决
    看懂项目代码需要掌握的技能 (java语言)
    jmeter响应断言通过,结果树中却显示红色
    nginx的upstream后端名称居然变成了请求的host了?
    基于QRcode创建和识别二维码的研究
    thinkphp访问mysql中文字段问题
    apache https访问配置
    如何获得bibitem格式的参考文献
    CSharp: Image Matting
    word2vec回顾
  • 原文地址:https://www.cnblogs.com/yuxiaole/p/9969360.html
Copyright © 2020-2023  润新知