DispatcherServlet请求流程解析-doDispatch(三)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 上篇文章我们主要看了DispatcherServlet在提供服务之间做的初始化工作,大部门工作都在WebApplicationContext中完成,然后WebApplicationContext是DispatcherServlet的一个属性。

上篇文章我们主要看了DispatcherServlet在提供服务之间做的初始化工作,大部门工作都在WebApplicationContext中完成,然后WebApplicationContext是DispatcherServlet的一个属性。

在初始操作完成以后,DispatcherServlet可以提供健全的服务了,早先我们也提到了,真正的请求分发在doDispatcher这个方法之中,今天一起来看看这个方法到底做了什么操作。

解析

看代码之前,先提前说明自己自己阅读源码过程中的一点简单思考:

  • WebAsyncManager在普通的开发中用不到,在异步处理的时候有用到,这一块我们先忽略
  • 关键部分:HandlerMappings,HandlerAdapter,HandlerExecutionChain对这几个熟悉之后基本上就懂了它到底在做什么

一起看代码吧

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 || mappedHandler.getHandler() == 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);
                }
            }
        }
    }
  1. processedRequest意思为处理之后的请求,而它是由checkMultipart(request)方法处理的,我们看下这个方法。
  • 判断是不是multipart的请求,默认的判断方式是先判断是不是"POST"请求,然后判断"contentType"是否以multipart开头。
  • 如果不满足multipart条件,直接返回request,也就是request没有做任何处理
  • 如果满足multipart基本条件, 将multipartRequest转换为StandardMultipartHttpServletRequest请求。
// 检查是否为multipart,如果是的话处理一下。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
       if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
       
               try {
                   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);
                       // Keep processing error dispatch with regular request handle below
                   }
                   else {
                       throw ex;
                   }
               }
           }
       }

       return request;
   }
   
   // 判断是否为multipart的方法
   public boolean isMultipart(HttpServletRequest request) {
       // Same check as in Commons FileUpload...
       if (!"post".equalsIgnoreCase(request.getMethod())) {
           return false;
       }
       String contentType = request.getContentType();
       return StringUtils.startsWithIgnoreCase(contentType, "multipart/");
   }


  //转换为multipart请求
   @Override
   public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
       return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
   }
  1. 获取request的Handler方法,handler被封装为HandlerExecutionChain,HandlerExecutionChain为handler方法加上拦截器的集合。我们定义的拦截器就在HandlerExecutionChain这个类中生效。

HandlerExecutionChain为真正的Handler对象与Interceptor的组合类

img_69d82739bbf2c9adf27e896dfcefddf9.png
image.png

HandlerExecutionChanin mappedHandler = getHandler(request),内部细节如下

  • 内部获取handler方法,在DispatcherServlet中大部分的处理都要将request或者response作为参数对象。
  • 内部获取handler的实现,获取request请求的路径,然后从mappingRegistry中拿到路径匹配的HandlerMethod. HandlerMethod封装了我们平时写的@RequestMapping等之类的方法,还有bean。这些可以通过反射获得
  • 获取到HandlerMethod之后,稍作处理封装handler和beanhandlerMethod.createWithResolvedBean()
  • 上面获取的是HandlerMethod,getHandler中再次封装handler和interceptors

这些都是为真正的处理请求做准备

@Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        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 = getApplicationContext().getBean(handlerName);
        }

      // 这一步的目的是就是,获取所有的interceptor的对象,然后与当前的handler组合在一起
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }
    
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        if (logger.isDebugEnabled()) {
            logger.debug("Looking up handler method for path " + lookupPath);
        }
        this.mappingRegistry.acquireReadLock();
        try {
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            if (logger.isDebugEnabled()) {
                if (handlerMethod != null) {
                    logger.debug("Returning handler method [" + handlerMethod + "]");
                }
                else {
                    logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
    
  1. 通过Handler获取HandlerAdpater,HandlerAdpater作为适配器,真正做东西的还是Handler,HandelAdpater做了很多的预处理,不过它的接口很简单。
public interface HandlerAdapter {
   // 判断当前的HandlerAdpater是否支持handler的实例,也就是当前的adapter能否使用传过来的Handler
    boolean supports(Object handler);
    // 使用传过来的handler来处理请求
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
    //和Servlet的getLastModified功能基本一致
    long getLastModified(HttpServletRequest request, Object handler);

}

在DispatcherServlet中getHandlerAdapter的实现,则是遍历HandlerAdpaters,然后返回第一个支持handler的Adpater.

img_11c73d042024c87135ec497fde41c153.png
image.png
  1. Handler的预处理,还记得刚才返回的HandlerExecutionChain封装了Handler和一些拦截器,现在就是调用拦截器的时候。

然后其内部就是我们写拦截器的时候常见的preHandler方法。如果拦截器preHndle返回false,那么请求终止。

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

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];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }


  1. 这个整个DispatcherServlet中最关键的一步,也可以说是最复杂的一步,单独提取出来也够有很多研究的。

    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

HandlerAdpater让Handler处理request与response,RequestMappingHandlerAdpater是最复杂的一个实现,也是大部分请求中都用到的一个Adpater.

  • 检查请求,checkRequest,判断请求是否合法
  • 判断是否要对invokeHandlerMethod加锁,不管是否加锁这个方法肯定会执行的。
  • 执行invokeHandlerMethod,记得HandlerMethod封装了请求路径映射的Method对象
img_18af5829be43f3a21203c4ab43c33fb4.png
image.png

invokeAndHandler内部调用的是invokeForReuquest,根据当前的请求获取Method对应的参数,然后进行invoke.


img_0cc854b830ef47583db27962017d15bf.png
image.png

argumentResolvers主要用来解析Method的参数,细节以后再看
doInvoke将解析的参数与方法进行invoke.


img_72c074421be3e6ebf93fdc9c3bfac387.png
image.png

在invokeMethod之后,设置Response的状态码,然后标记请求已经被处理了。


img_7886f34674a967bf018875de7477ff62.png
image.png

有关getModelAndView的细节,后续再看

这一步已经完成了请求的处理。

  1. applyDefaultViewName,然后进行拦截器的请求完成处理mappedHandler.applyPostHandle(processedRequest, response, mv);这一步与前面的拦截器预处理类似。

  2. 处理请求分发的结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);看其中的代码也没有多复杂,相对容易理解

  • 检查处理请求的时候是否有异常抛出,前面如果有异常抛出会被捕获,然后传递到这一步,然后根据异常处理错误页面
  • 如果ModelAndView有值,不管是正常显示还是错误页面,进行渲染
  • 做最后的清楚工作,并且执行拦截器的完全执行完成操作
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) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }

        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
  1. 清除现场工作,如果是multipart的请求,清空上传的multipart.


    img_e5f8dd776dc8f40de0e441bbe984374d.png
    image.png

回顾

整个流程看起来虽然不复杂,内部细节很多,更多的了解细节,后面写代码的时候如果出现了错误排除问题才更方便。

简单来说,重要的组件有HandlerMappings,HandlerAdpaters,HandlerExecutionChain,Handler, 最后还有一些视图处理相关技术。

  • 首先获取HandlerExecutionChain,其内部封装了Handler与Method,Interceptor
  • 然后获取HandlerAdpater,根据是否support来获取,有顺序的,获取的是第一个
  • HandlerExecutionChain的拦截器进行preHandler
  • HandlerAdpater处理Handler,Request,Response,内部依靠反射激发我们编写的Bean调用
  • HandlerExecutionChain的拦截器postHandler
  • 处理dispatcher的结果,对视图相关进行处理
  • 最后的清除工作

总结

Spring源码博大精深,看源码的过程,吸收一些大师的写法,学到知识的过程是比较爽的。希望能和大家一起讨论。

相关文章
|
2月前
|
JavaScript 前端开发 开发者
Vue执行流程及渲染解析
【10月更文挑战第2天】
109 58
|
2月前
|
JavaScript 前端开发 UED
Vue执行流程及渲染解析
【10月更文挑战第5天】
|
2月前
|
存储 搜索推荐 数据库
运用LangChain赋能企业规章制度制定:深入解析Retrieval-Augmented Generation(RAG)技术如何革新内部管理文件起草流程,实现高效合规与个性化定制的完美结合——实战指南与代码示例全面呈现
【10月更文挑战第3天】构建公司规章制度时,需融合业务实际与管理理论,制定合规且促发展的规则体系。尤其在数字化转型背景下,利用LangChain框架中的RAG技术,可提升规章制定效率与质量。通过Chroma向量数据库存储规章制度文本,并使用OpenAI Embeddings处理文本向量化,将现有文档转换后插入数据库。基于此,构建RAG生成器,根据输入问题检索信息并生成规章制度草案,加快更新速度并确保内容准确,灵活应对法律与业务变化,提高管理效率。此方法结合了先进的人工智能技术,展现了未来规章制度制定的新方向。
36 3
|
2月前
|
存储 缓存 边缘计算
揭秘直播带货背后的黑科技:播放流程全解析!
大家好,我是小米,今天聊聊社区直播带货的技术细节。我们将探讨直播播放流程中的关键技术,包括 HTTP DASH 协议、POP(Point of Presence)缓存和一致性哈希算法等。通过这些技术,直播流能根据网络状况动态调整清晰度,保证流畅体验。POP 和 DC 的多层次缓存设计减少了延迟,提升了观看效果。无论是技术人员还是直播运营者,都能从中受益。希望通过本文,你能更好地理解直播背后的技术原理。
49 3
|
2月前
|
前端开发 JavaScript UED
axios取消请求CancelToken的原理解析及用法示例
axios取消请求CancelToken的原理解析及用法示例
118 0
|
2月前
|
敏捷开发 数据可视化 测试技术
解析软件项目管理:以板栗看板为例,其如何有效影响并优化软件开发流程
软件项目管理是一个复杂而重要的过程,涵盖了软件产品的创建、维护和优化。其核心目标是确保软件项目能够顺利完成,同时满足预定的质量、时间和预算目标。本文将深入探讨软件项目管理的内涵及其对软件开发过程的影响,并介绍一些有效的管理工具。
|
15天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
70 0
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
2月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
62 0