DispactherServlet请求流程解析(一)

关于DispacherServlet的请求流程,每天都在用,但其内部的细节,了解多少呢?

Servlet介绍

DispacherServlet也是Servlet的一种,想弄明白它,对Servlet要有一定的了解。Servlet是一套规范,我们按照规范编写的代码可以直接在Servlet容器内运行。但我们先从高层讲起,假设读者对Servlet心里有个大概。

完整流程

  1. 初始化操作,在容器启动的时候,首先进入Servlet的init(ServletConfig config)方法,这是的Servlet接口的基本方法。所有Servlet启动的时候都要有的。

在Servlet的API中,其交给了GenericServlet来实现,而GenericServlet将其交给init()无参方法实现。

DispacherServelt中的init方法使用的是其父类HttpServletBean的init方法。我们来看看HttpServletBean的初始化做了什么事情。

/**
     * Map config parameters onto bean properties of this servlet, and
     * invoke subclass initialization.
     * @throws ServletException if bean properties are invalid (or required
     * properties are missing), or if subclass initialization fails.
     * 
     * 映射Servlet的配置参数到Bean的属性上,然后交给其子类进行初始化。如果子类初始化失败,则抛出ServletException。
     */
    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }

        // Let subclasses do whatever initialization they like.
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

解释:

  • 首先获取配置,如果再Servlet容器,比如说Tomcat中,获取配置的Servlet参数,我在web.xml中配置了一个采纳数,经过new ServletConfigPropertyValues之后,获取到了参数。

关于属性非空后,内部如何处理,后期再深入研究,现在往下看,进行initServletBean()操作

image.png
image.png
  1. 初始化ServletBean,这一步进入到了FrameworkServlet中。在HttpServlet设置属性之后调用,创建Servlet的WebApplicationContext。然后初始化ServletFramework,初始化FramworkServlet是个空方法,留待自己补充。
/**
     * Overridden method of {@link HttpServletBean}, invoked after any bean properties
     * have been set. Creates this servlet's WebApplicationContext.
     * 
     */
    @Override
    protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();
    
     // 初始化WebApplication环境
        try {
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

初始化initWebApplicationContext,又包含了很多的内容,这个留着下次再说,先把大体思路搞清楚。先给读者瞅一眼初始化的操作

image.png

到这一步,DispacherServlet的初始化工作算是完成了,听名字可以理解为什么叫做DispacherServlet了,因为它并没有做初始化的工作,它主要负责分发(Dispacher)请求和响应。

  1. 处理真正的请求。我们知道Servlet处理请求,调用的是service方法。DispacherServlet的service(ServletRequest req, ServletResponse res),由HttpServlet来处理,其把请求和响应分别封装为HttpServletRequest,HttpServletResponse。
 public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }
  1. 处理service(HttpServletRequest req, HttpServletResponse resp),HttpServlet的子类覆盖了这个方法,因此会先调用FrameworkServlet的service方法。

FrameworkServlet的service方法主要拦截Patch方法或者null的HttpMethod

/**
     * Override the parent class implementation in order to intercept PATCH requests.
     */
    @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收到的是常规的HTTP方法,再转给父类HttpServelt来处理,HttpServlet的 service(HttpServletRequest req, HttpServletResponse resp)只是判断HttpMethod,然后进行相应的doXXX操作,doGet,doPost,doPut等。瞅一眼看看


image.png
  1. 上一步提到,所有的HttpServlet将方法转给doXXX来操作,其父类FrameworkServlet将所有的doXXX操作,又交给了processRequest来处理,感受下。
image.png

那么真正这一步要看的是processRequest做了什么操作。处理请求,不管处理结果如何都会发布事件,发布的事件由doService模板方法处理。

    /**
     * Process this request, publishing an event regardless of the outcome.
     * <p>The actual event handling is performed by the abstract
     * {@link #doService} template method.
     */
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
      // i18n的操作
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

     // 获取绑定到当前线程的请求属性
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 创建Servelet请求属性,这些都是在Spring框架中的内容
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
        
      // 获取当前请求的asyncManager   
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        
        //注册请求的拦截器
            asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    
     //初始化ContextHodlers
        initContextHolders(request, localeContext, requestAttributes);

        try {
          
            doService(request, response);
        }
        catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }

            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

  1. DispatcherServlet的doService方法,上步说到FrameworkServlet的processRequest一定会调用doService方法,而FrameworkServlet自己并没有实现doService方法,最终交给了DispatcherServlet来实现。

我们看看DispatcherServlet的处理,终于等到真正关键的类出场了,进去之后发现,这个类做的事情都是设置属性,最后转给doDispatch处理。

// 暴露DispatcherServlet-specific的请求属性给doDispatch来处理   
@Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                    " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<String, Object>();
            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));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, 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 {
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }
  1. doDispatch处理真正的分发到处理器Handler上面,handler会按顺序获得servlet的HandlerMappings,然后获得HandlerAdapter会查询第一个支持的handler类。

    所有的Http方法都由这个方法来处理,

        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 记得上上一步是processRequest处理的
        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.
                //获取当前请求的Handler, 方法为循环遍历handlerMappings,然后找到request对应的handler。   
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {  
                  // 没有handler找到错误
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                // 找到handler的adapter,循环遍历adapter,判断adpater是否支持对应的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;
                    }
                }

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

                // Actually invoke the handler.
                // 使用HandlerAdapter处理请求。普通的请求是RequestMappingHandlerAdapter。将请求转换为ModelandView
                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);
                }
            }
        }
    }

普通的请求是RequestMappingHandlerAdapter。


image.png

这一步涉及到DispatcherServlet的几大组件,其内部相对复杂,一下子不方便解释完整。现在能GET到的点

首先获取Handler

mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

Handler处理请求

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

处理请求结果

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

当然除了DispatcherServelt的finally做了善后工作,内部很多也做了finally的善后。

回顾

梳理下这次分析源码所获得的知识,首先Servlet容器会调用init方法,而Spring MVC中init方法交给的是HttpServlet,HttpServletBean等做了处理。

在真正提高服务的方法上面,HttpServlet,FramworkServlet主要对请求做一些预处理,processRequest,然后交给DispatcherServelt的doSerice方法,doSerice又对分发做了一些准备,在request中做了一些设置,最后由DispatcherServelt做分发,真正的分发处理由Handler处理。

小结

Spring源码博大精深,短短一些话说不完,后面再持续更新Spring MVC的处理细节。

我想说细节太多,建议一起探索,可以相互交流奥

参考

Spring源码

TODO

  • 在Servlet初始化之后,Spring框架对初始配置属性做的处理
  • initWebApplicationContext的细节
  • DispatcherServelt中的Handler处理细节
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容