Spring MVC 执行流程

一个请求 url 是怎么样找到 Handler 进行处理的?拦截器为何 preHandler 顺序执行,postHandler 就倒序执行?Spring MVC 是怎么样去优雅的处理异常的?

调试环境:https://gitee.com/jhxxb/MySpringBoot/tree/master/Spring-Base-Web/mvc-ssm-annotation

 

源码

FrameworkServlet 复写了 Servlet 的 service 方法

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    /**
     * 为了支持到 PATCH 请求(PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行局部更新,目前使用得非常少)
     * 源生的 servlet 并不支持 PATCH 请求
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
            processRequest(request, response);
        } else {
            super.service(request, response);
        }
    }

    /**
     * FrameworkServlet 复写所有的 doGet/doPost 等等都交给了 processRequest(request, response);方法
     */
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null; // 记录抛出的异常(若有的话)

        // 拿到之前的 LocaleContext 上下文(因为可能在 Filter 里已经设置过了)
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        // 以当前的 request 创建一个 Local 的上下文,后面会继续处理
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 这里面 build 逻辑注意:previousAttributes 若为 null,或者就是 ServletRequestAttributes 类型,那就 new ServletRequestAttributes(request, response);
        // 若不为 null,就保持之前的绑定结果,不再做重复绑定
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        // 拿到异步管理器。这里是首次获取,会 new WebAsyncManager(),然后放到 request 的 attr 里面
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        // 这里需要注意:给异步上下文恒定注册了 RequestBindingInterceptor 这个拦截器(作用:绑定当前的 request、response、local 等)
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

        // 把 request 和 Local 上下文、RequestContext 绑定
        initContextHolders(request, localeContext, requestAttributes);

        try {
            doService(request, response); // 模版设计模式:由子类 DispatcherServlet 去实现实际逻辑
        } catch (ServletException | IOException ex) {
            failureCause = ex;
            throw ex;
        } catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        } finally { // 这个时候已经全部处理完成,视图已经渲染了
            // doService() 方法完成后,重置上下文,也就是解绑
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            logResult(request, response, failureCause, asyncManager);
            // 关键:不管执行成功与否,都会发布一个事件,说我处理了这个请求(有需要监听的,就可以监听这个事件,每次请求都会有)
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

    private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) {
        if (this.publishEvents && this.webApplicationContext != null) { // 当 publishEvents 设置为 true 和 webApplicationContext 不为空就会处理这个事件的发布
            // 计算出处理该请求花费的时间
            long processingTime = System.currentTimeMillis() - startTime;
            this.webApplicationContext.publishEvent( // ServletRequestHandledEvent 这个事件:目前来说只有这里会发布
                    new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(),
                            request.getMethod(), getServletConfig().getServletName(),
                            WebUtils.getSessionId(request), getUsernameForRequest(request),
                            processingTime, failureCause, response.getStatus()));
        }
    }

FrameworkServlet 把主要处理逻辑交给了 doService 方法,DispatcherServlet 覆写了 doService 方法

public class DispatcherServlet extends FrameworkServlet {
    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);

        // 如果该请求是 include 的请求(请求包含) 那么就把 request 域中的数据保存一份快照版本
        // 等 doDispatch 结束之后,会把这个快照版本的数据覆盖到新的 request 里面去
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // 把一些常用对象放进请求域,方便 Handler 里面可以随意获取
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); // web 子容器
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        if (this.flashMapManager != null) { // 如果是重定向,放置得更多一些
            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 {
            doDispatch(request, response); // DispatcherServlet 最重要的方法,分发请求、找到 handler 处理等
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                if (attributesSnapshot != null) { // 如果是 include 请求,会将上面的数据快照重新放置到 request 里面去
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request; // 此处用 processedRequest 需要注意的是:若是处理上传,processedRequest 将和 request 不再指向同一对象
        HandlerExecutionChain mappedHandler = null; // 异常链处理器
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                // checkMultipart 这个方法很重要,判断是否是上传需求。且看下面的具体分析
                // 如果请求是 POST 请求,并且请求头中的 Context-Type 是以 multipart/ 开头的就认为是文件上传的请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request); // 标记一下是否是文件上传的 request

                // 找到一个处理器,如果没有找到对应的处理类的话,这里通常会返回 404,如果 throwExceptionIfNoHandlerFound 属性值为 true 的情况下会抛出异常
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 根据实际的 handler 去找到一个合适的 HandlerAdapter,方法逻辑同 getHandler
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 如果是 GET 请求,如果内容没有变化的话,则直接返回
                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;
                }

                // 真正执行我们自己书写的 controller 方法的逻辑。返回一个 ModelAndView
                // 这也是一个很复杂的过程(序列化、数据绑定等等)
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) { // 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器 PostHandle 之类的
                    return;
                }

                applyDefaultViewName(processedRequest, mv); // 如果我们没有设置 viewName,就采用默认的。否则采用我们自己的
                // 执行所有的拦截器的 postHandle 方法,并且把 mv 给它,这里有一个小细节:这个时候拦截器是【倒序】执行的
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception ex) { // 这两个 catcher 什么都不做,只是把异常记录下来
                dispatchException = ex;
            } catch (Throwable err) {
                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);
                }
            }
        }
    }

 

DispatcherServlet 中的处理

文件上传判断

public class DispatcherServlet extends FrameworkServlet {
    /**
     * multipartResolver 初始化的值可能为 null(如果没有配置对应的 Bean 的话)
     */
    protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        // 配置了 multipartResolver,并且是文件上传的请求,才会继续往下走
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            // 如果该请求已经是 MultipartHttpServletRequest,那就输出一个日志
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
                if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
                    logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
                }
            } else if (hasMultipartException(request)) { // 判断是否有 MultipartException,一般没有
                logger.debug("Multipart resolution previously failed for current request - skipping re-resolution for undisturbed error rendering");
            } else {
                try {
                    // 这里特别注意,不管是哪种 multipartResolver 的实现,内部都是 new 了一个新的 MultipartHttpServletRequest 的实现类,不再指向原来的 request 了
                    return this.multipartResolver.resolveMultipart(request);
                } catch (MultipartException ex) {
                    if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
                        logger.debug("Multipart resolution failed for error dispatch", ex);
                    } else {
                        throw ex;
                    }
                }
            }
        }
        // 如果前面没有返回,就原样返回,什么都不做
        return request;
    }

这里需要注意的是:org.springframework.web.multipart.support.MultipartFilter,如果在 web.xml 中配置这个过滤器的话,则会在过滤器中提前判断是不是文件上传的请求,并将请求转换为 MultipartHttpServletRequest 类型。

这个过滤器中默认使用的 MultipartResolver 为 StandardServletMultipartResolver。

在 CommonsMultipartResolver 中有一个属性叫 resolveLazily,这个属性值代表是不是延迟解析文件上传,默认为 false。最终返回的是一个 DefaultMultipartHttpServletRequest 的类。这里有一个重要的方法是:parseRequest,这个方法干的事是解析文件上传请求。它的底层是 commons-fileupload 那一套,不同的是 Spring 在获取 FileItem 之后,又进行了一下封装,封装为了便于 Spring 框架整合。

 

获取 Handler

https://www.cnblogs.com/jhxxb/p/14167345.html

public class DispatcherServlet extends FrameworkServlet {
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            // 把所有配置的 HandlerMapping 都拿出来查找,只要找到一个就返回
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

SpringMVC 默认加载三个请求处理映射类:RequestMappingHandlerMapping、SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping

这三个类有一个共同的父类:AbstractHandlerMapping。在上面代码中 mapping.getHandler(request) 这个 getHandler 方法在 AbstractHandlerMapping 中,它的子类都没有重写这个方法。

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 这个是留给子类去重写实现的:查找 handler 处理器的,比如根据 URL 去查找匹配等等
        // 备注:获取 hadnler 的过程,非常的复杂
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        // 构建出一个处理器链,注意:和 handler 绑定了,并且内部还去拿到了所有的拦截器,然后添加到处理器连里面去
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }

        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { // 是不是 cors 跨域请求
            CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            config = (config != null ? config.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

 

执行拦截器链

public class HandlerExecutionChain {
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                // 如果拦截器返回了 false,就立马触发所有拦截器的 AfterCompletion 方法。并且马上 return false
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = 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 ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }

 

处理请求结果

public class DispatcherServlet extends FrameworkServlet {
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception)
throws Exception { boolean errorView = false; if (exception != null) { // 如果有异常,就进入异常处理逻辑,返回到异常页面 if (exception instanceof ModelAndViewDefiningException) { // 含有异常页面视图的异常 logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); // 1、会执行我们自己配置(或者默认配置)的所有 HandlerExceptionResolver 处理器 // 2、上面需要注意了,但凡处理方法返回的不是 null,有 mv 的返回。那后面的处理器就不会再进行处理了。具有短路的效果,要注意,是通过 null 来判断的 // 3、处理完成后,得到 error 的视图 mv,最后会设置一个 viewName,然后返回出去 mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } if (mv != null && !mv.wasCleared()) { // 若视图不为空,不为 null,就开始执行 render() 方法渲染视图 render(mv, request, response); if (errorView) { // 如果有错误视图,这里清除掉所有请求域里的所有错误属性 WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } // 处理异步,注意它不执行后面的 AfterCompletion 方法了 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return; } if (mappedHandler != null) { // 执行拦截器的 AfterCompletion 方法 mappedHandler.triggerAfterCompletion(request, response, null); } } protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // 通过 localeResolver 把 local 解析出来,放到 response 里去 Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null) { // 如果已经有 viewName 了(绝大多数情况) // 视图解析器,根据 String 类型的名字,解析出来一个视图(视图解析器有多个) // 还是那个原理:只要有一个返回了不为 null 的,后面的就不会再解析了 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null) { // 如果解析不出来视图,那就抛出异常,说不能解析该视图 throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // 没有视图名称,但是必须有视图内容,否则抛出异常 view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } if (logger.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] "); } try { if (mv.getStatus() != null) { // 设置响应码 status response.setStatus(mv.getStatus().value()); } // 根据 model 里的数据,正式渲染 view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]", ex); } throw ex; } }

 


https://blog.csdn.net/f641385712/article/details/87982095

https://www.bilibili.com/video/BV19K4y1L7MT

posted @ 2019-02-26 14:58  江湖小小白  阅读(181)  评论(0)    收藏  举报