springboot內(nèi)嵌tomcat代碼走讀(四)

上篇文章我們走讀了servlet生命周期中的創(chuàng)建和初始化以及servlet中Filter在springboot中的使用。本文繼續(xù)進行servlet生命周期中下面的部分,提供服務(wù)即service方法屯碴。
Servlet的service方法在HttpServlet中實現(xiàn)朴爬,代碼為:

    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        service(request, response);
    }
    @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);
        }
    }
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

羅列了上面三段代碼后顿痪,發(fā)現(xiàn)帮毁,我們就進入到了doXXX方法了幌氮,這里眷蜓,我們選doGet方法來走讀代碼故俐。
doGet方法的實現(xiàn)在類FrameworkServlet中想鹰。

    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        ......
        try {
            //真正執(zhí)行操作。
            doService(request, response);
        }
        catch (ServletException | 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();
            }
            logResult(request, response, failureCause, asyncManager);
            //廣播一個請求處理完成的事件药版,這個是一個可擴展的口子
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

DispatcherServlet中實現(xiàn)了doService.

@Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ......
        // 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());

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

        RequestPath requestPath = null;
        if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
            requestPath = ServletRequestPathUtils.parseAndCache(request);
        }

        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);
                }
            }
            if (requestPath != null) {
                ServletRequestPathUtils.clearParsedRequestPath(request);
            }
        }
    }

doDispatch方法實現(xiàn):

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) {
                    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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                //我們定義的interceptor的preHandler方法調(diào)用
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                //真正調(diào)用處理器,返回modelAndView
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                //調(diào)用interceptor的postHandler方法槽片。
                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);
            }
            //處理結(jié)果何缓,將ModelAndView解析成view,或者異常的處理
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //inteceptor中preHandler和postHandler一一對應(yīng)
            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);
                }
            }
        }
    }

接下來还栓,我們看每步操作的具體實現(xiàn)碌廓。

Handler的獲取

先看getHandler方法的代碼:

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

這里從handlerMappings中獲取mapping,handlerMappings值是有順序的剩盒,排第一的一般是RequestMappingHandlerMapping,這里handlerMappings在前文中說的initHandlerMappings里做的初始化谷婆,主要取HandlerMapping類型的bean。默認情況下勃刨,springboot里有5個波材,分別為RequestMappingHandlerMapping,WelcomePageHandlerMapping,BeanNameUrlHandlerMapping,RouterFunctionMappingSimpleUrlHandlerMapping,這些bean定義在WebMvcConfiguration及其父類中。這里就不做過詳細的贅述了身隐。
RequestMappingHandlerMapping比較特殊一些廷区,這里拿出來詳細看一下其bean的初始化過程,這個跟getHandler有關(guān)系贾铝。
先看類圖:

RequestMappingHandlerMapping.png

RequestMappingHandlerMapping實現(xiàn)了InitializingBean接口隙轻,就是說在該bean初始化完成后,會調(diào)用afterPropertiesSet進行后置處理垢揩。
我們來看該方法的實現(xiàn):

    public void afterPropertiesSet() {

        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setTrailingSlashMatch(useTrailingSlashMatch());
        this.config.setContentNegotiationManager(getContentNegotiationManager());

        if (getPatternParser() != null) {
            this.config.setPatternParser(getPatternParser());
            Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
                    "Suffix pattern matching not supported with PathPatternParser.");
        }
        else {
            this.config.setSuffixPatternMatch(useSuffixPatternMatch());
            this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
            this.config.setPathMatcher(getPathMatcher());
        }

        super.afterPropertiesSet();
    }

這里會調(diào)用父類的afterPropertiesSet方法玖绿。

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
    protected void initHandlerMethods() {
        //遍歷工廠中所有的bean
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                //處理選中的bean
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
        if (beanType != null && isHandler(beanType)) {
            detectHandlerMethods(beanName);
        }
    }
    @Override
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

只有被Controller或RequestMapping注解的bean,才被當(dāng)成處理器叁巨。即需要業(yè)務(wù)實現(xiàn)的bean斑匪,也就是我們平時寫的controller。

protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            if (logger.isTraceEnabled()) {
                logger.trace(formatMappings(userType, methods));
            }
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

registerHandlerMethod會調(diào)用AbstractHandlerMethodMapping的register方法锋勺,將handler注冊到mapping中蚀瘸。
此后,使用getHandler時庶橱,就能得到對應(yīng)的handler了贮勃。返回頭來,再繼續(xù)看getHandler方法苏章。

先看getHandler方法的代碼:

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

這里寂嘉,for循環(huán)里第一個一般取的是RequestMappingHandlerMapping奏瞬,看RequestMappingHandlerMapping的getHandler方法。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //在mappingRegistry中獲取到處理器泉孩。
        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);
        }
        //封裝成HandlerExecutionChain返回硼端。
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        ......
        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            CorsConfiguration config = getCorsConfiguration(handler, request);
            if (getCorsConfigurationSource() != null) {
                CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
                config = (globalConfig != null ? globalConfig.combine(config) : config);
            }
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

至此,就獲取到一個mappedHandler寓搬。下面显蝌,我們看如何獲取處理器適配器。

HandlerAdapter的獲取

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());在工廠中獲取HandlerAdapter類型的bean订咸。bean的定義也在WebMvcAutoConfiguration及其父類中。

具體的業(yè)務(wù)處理

mappedHandler和HandlerAdapter都獲取完了酬诀,接下來就是具體的業(yè)務(wù)處理了脏嚷。首先處理interceptor的preHandler。接下來通過HandlerAdapter的handle方法通過反射的方式調(diào)用具體的處理器里的接口瞒御,返回ModelAndView父叙,然后處理interceptor的postHandler。最后解析ModelAndView進行視圖渲染肴裙。

自此趾唱,客戶端請求在springboot中的流轉(zhuǎn)全部完成。后面代碼邏輯比較清晰蜻懦,就沒有專門貼代碼來做詳細的說明了甜癞。以后用到的時候再進行說明吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宛乃,一起剝皮案震驚了整個濱河市悠咱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌征炼,老刑警劉巖析既,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谆奥,居然都是意外死亡眼坏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門酸些,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宰译,“玉大人,你說我怎么就攤上這事擂仍《谝伲” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵逢渔,是天一觀的道長肋坚。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么智厌? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任诲泌,我火速辦了婚禮,結(jié)果婚禮上铣鹏,老公的妹妹穿的比我還像新娘敷扫。我一直安慰自己,他們只是感情好诚卸,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布葵第。 她就那樣靜靜地躺著,像睡著了一般合溺。 火紅的嫁衣襯著肌膚如雪卒密。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天棠赛,我揣著相機與錄音哮奇,去河邊找鬼。 笑死睛约,一個胖子當(dāng)著我的面吹牛鼎俘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辩涝,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼贸伐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膀值?” 一聲冷哼從身側(cè)響起棍丐,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沧踏,沒想到半個月后歌逢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡翘狱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年秘案,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潦匈。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡阱高,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茬缩,到底是詐尸還是另有隱情赤惊,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布凰锡,位于F島的核電站未舟,受9級特大地震影響圈暗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裕膀,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一员串、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昼扛,春花似錦寸齐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛹含,卻和暖如春海铆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挣惰。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留殴边,地道東北人憎茂。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像锤岸,于是被迫代替她去往敵國和親竖幔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351