Spring總結(jié)

Spring知識(shí)框架.png

1.Spring核心

今天給大家講講Spring吧,談到Spring,學(xué)過(guò)的人估計(jì)第一反應(yīng)就是IOC和AOP,的確,這兩個(gè)就是Spring的核心知識(shí),也可以說(shuō)是一種思想理念。下面我們就來(lái)具體講講這兩個(gè)東西到底是什么吧~
1.IOC(控制反轉(zhuǎn)):顧名思義,意思就是將我們控制Bean對(duì)象的權(quán)利反轉(zhuǎn)交給Spring來(lái)控制和管理。那么Spring是怎么控制Bean的呢?眾所周知,通常我們創(chuàng)建一個(gè)對(duì)象一般都是要用的時(shí)候new一個(gè)出來(lái),在還沒(méi)有出現(xiàn)IOC之前,這種方式習(xí)以為常,但是當(dāng)IOC出現(xiàn)之后,再去使用new這種方式寫代碼,大家肯定有一種非常難受的感覺(jué)。Spring用一個(gè)注解能解決的事情使得大家就不用再去大費(fèi)周章new一個(gè)對(duì)象了。
如何使用IOC?
這個(gè)問(wèn)題其實(shí)就是再問(wèn)我們?nèi)绾稳プ?cè)那些我們所需要的Bean對(duì)象,Spring 提供了四種方式供大家使用,分別是XML/注解/Java Config/Groovy DSL;前三種是我們經(jīng)常使用的方法,我就不過(guò)多解釋了。后面那種是基于DSL語(yǔ)言的,可以把它通俗理解為一個(gè)偽class文件,如果有接觸過(guò)protocol buff格式數(shù)據(jù)或者是Grpc框架的同學(xué)應(yīng)該知道,我們?cè)谶M(jìn)行java與python系統(tǒng)交互的時(shí)候,會(huì)將java對(duì)象轉(zhuǎn)換生成一個(gè)proto文件,Groovy DSL方式大概就是這個(gè)意思了——自定義一個(gè)對(duì)象文件。
優(yōu)勢(shì):
1.提供了一套管理bean的生命周期模式
2.單例模式
3.方便測(cè)試
4.屏蔽對(duì)象創(chuàng)建的細(xì)節(jié)
2.AOP(面向切口編程):在Spring中,我們還是會(huì)經(jīng)??吹紸OP的身影的,比如在我們實(shí)現(xiàn)一些非業(yè)務(wù)代碼時(shí)——日志記錄、接口記錄、接口使用情況等。它的底層原理是動(dòng)態(tài)代理,在spring中依賴的是beanPostProcessor。一般都是配合注解使用。在我們的項(xiàng)目中,為了響應(yīng)網(wǎng)監(jiān)的監(jiān)管,我就是使用AOP+自定義注解的方式實(shí)現(xiàn)了一套用戶ip地址記錄和詳細(xì)用戶數(shù)據(jù)日志記錄。

2.Bean

下面就來(lái)詳細(xì)介紹一下在Spring中Bean對(duì)象創(chuàng)建到消亡的整個(gè)過(guò)程,話不多少,直接上圖!

bean生命周期.png

具體流程:spring在啟動(dòng)的時(shí)候,首先加載配置文件、注解、config中需要?jiǎng)?chuàng)建的bean,把這些元數(shù)據(jù)和相關(guān)信息封裝成DefineBean對(duì)象,然后放入一個(gè)DefineBeanMap中,緊接著遍歷這個(gè)DefineBeanMap,根據(jù)工廠模式思想,執(zhí)行BeanFactoryPostProcesor前置處理器的方法,創(chuàng)建相關(guān)bean對(duì)象的工廠類,這里可以對(duì)對(duì)象的信息做一些修改操作。然后工廠類開(kāi)始創(chuàng)建bean對(duì)象,下一步屬性注入,當(dāng)bean對(duì)象創(chuàng)建完成之后,接下來(lái)就要考慮實(shí)例化了;這時(shí),首先檢查該對(duì)象是否實(shí)現(xiàn)了Aware相關(guān)的接口,如果實(shí)現(xiàn)了,那么就要去填充相關(guān)資源。其次,再考慮執(zhí)行BeanPostProcessor后置處理器里面的擴(kuò)展方法,這里我們可以實(shí)現(xiàn)自己的方法,具體看業(yè)務(wù)邏輯是否需要!最后就是執(zhí)行初始化相關(guān)的方法了,init相關(guān)方法——如PostConstrut、實(shí)現(xiàn)了 InitializingBean接口的類、init-method方法等。創(chuàng)建完成!(順便提一下資源的銷毀,實(shí)現(xiàn)disposable的desory()方法,或者自定義銷毀方法)

這里需要注意一點(diǎn),在做依賴注入時(shí),可能會(huì)遇上循環(huán)依賴的問(wèn)題,Spring又是如何解決循環(huán)依賴問(wèn)題的呢?(補(bǔ)充:循環(huán)依賴可以理解為創(chuàng)建對(duì)象A需要依賴對(duì)象B,而對(duì)象B又需要依賴對(duì)象A)
答案是三級(jí)緩存!看圖

三級(jí)緩存.png

從上面的流程可以知道,Spring在創(chuàng)建對(duì)象的時(shí)候,是和屬性注入分開(kāi)的,而且在創(chuàng)建之前是先創(chuàng)建該對(duì)象對(duì)應(yīng)的工廠類,所以,我們從這里就可以將bean創(chuàng)建分為三個(gè)緩存階段,也就是三個(gè)Map,在對(duì)象A創(chuàng)建的時(shí)候,首先在三級(jí)緩存中生成一個(gè)對(duì)象map,key為對(duì)象名,值為對(duì)象。發(fā)現(xiàn)需要依賴對(duì)象B,那么這時(shí)會(huì)先去創(chuàng)建對(duì)象B,在對(duì)象B創(chuàng)建的時(shí)候,會(huì)去get對(duì)象A,A已經(jīng)存在,這時(shí)我們可以把對(duì)象B從三級(jí)緩存中移除,放入二級(jí)緩存,而對(duì)象A自然而然就可以生成了,再去做屬性注入,放入一級(jí)緩存。


提一下,這里為什么要考慮三級(jí)緩存,而不是二級(jí)緩存!
從三級(jí)緩存中,我們能夠拿到的代理對(duì)象,這樣方便管理對(duì)象
從二級(jí)緩存中,我們不用每次獲取對(duì)象都去工廠類中生成,提高性能

3.Spring MVC

如果說(shuō)spring是讓我們和系統(tǒng)對(duì)象打交道,那么MVC就是在和用戶打交道!我們可以理解為一個(gè)用戶發(fā)起一個(gè)請(qǐng)求,MVC封裝了中間所有的通訊細(xì)節(jié),找到這個(gè)請(qǐng)求想要的數(shù)據(jù),并封裝成一個(gè)ModelAndView對(duì)象返回給用戶。
1.交互流程

MVC運(yùn)行流程.png

總結(jié)一下:
①用戶發(fā)起url請(qǐng)求
②DispatcherServlet處理并分發(fā)請(qǐng)求到handleMapping
②handleMapping根據(jù)url找到相應(yīng)的handle
③handle查找適配器
④適配器調(diào)用對(duì)應(yīng)的handle方法處理請(qǐng)求
⑤將響應(yīng)結(jié)果封裝成ModleAndViewdu對(duì)象返回
⑥通過(guò)dispatcherservlet將處理好的視圖對(duì)象返回給用戶

2.核心源碼解析
根據(jù)上面講解的運(yùn)行流程,大家?guī)е@個(gè)思路邏輯看一下核心代碼

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 定義一個(gè)已處理請(qǐng)求,指向參數(shù)的request
        HttpServletRequest processedRequest = request;
        // 定義處理器執(zhí)行連,內(nèi)部封裝攔截器列表和處理器
        HandlerExecutionChain mappedHandler = null;
        // 是否有文件上傳的請(qǐng)求標(biāo)志
        boolean multipartRequestParsed = false;

        // 獲取異步管理器,執(zhí)行異步操作
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            // 保存處理器執(zhí)行的返回結(jié)果
            ModelAndView mv = null;
            // 保存處理過(guò)程中的異常
            Exception dispatchException = null;

            try {
                // 判斷當(dāng)前請(qǐng)求是否有上傳需求,并返回保存到processedRequest中
                processedRequest = checkMultipart(request);
                // 判斷當(dāng)前請(qǐng)求是否是文件上傳的請(qǐng)求,如果是則說(shuō)明是上傳請(qǐng)求已經(jīng)處理
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                // 獲取可處理當(dāng)前請(qǐng)求的請(qǐng)求處理器,通過(guò)HandlerMapping進(jìn)行查找
                mappedHandler = getHandler(processedRequest);
                // 如果沒(méi)有,就執(zhí)行沒(méi)有處理器的邏輯
                if (mappedHandler == null) {
                    // 在內(nèi)部處理中拋出異?;蛘叻祷?04
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                // 根據(jù)當(dāng)前請(qǐng)求的處理器獲取支持該處理器的適配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                // 處理last-modified請(qǐng)求頭,用于判斷請(qǐng)求內(nèi)容是否發(fā)生修改
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                // 只有g(shù)et請(qǐng)求和head請(qǐng)求執(zhí)行此判斷
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                // 通過(guò)mappedHandler這個(gè)HandlerExecutionChain執(zhí)行鏈的封裝,鏈?zhǔn)綀?zhí)行所有連接器的前置攔截方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    // 任意一個(gè)攔截器的前置攔截方法返回false,提前結(jié)束請(qǐng)求的處理
                    return;
                }

                // Actually invoke the handler.
                // 執(zhí)行處理適配器的處理方法,傳入請(qǐng)求,對(duì)請(qǐng)求進(jìn)行處理,此方法的返回值是ModelAndView對(duì)象,封裝了模型和視圖
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                // 如果是異步處理,則直接返回,后續(xù)處理通過(guò)異步執(zhí)行
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                // 返回的mv對(duì)象中如果沒(méi)有視圖名稱,則根據(jù)請(qǐng)求設(shè)置默認(rèn)視圖名
                applyDefaultViewName(processedRequest, mv);
                // 請(qǐng)求處理正常完成,鏈?zhǔn)綀?zhí)行所有攔截器的后置方法
                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.
                // 4.3版本之后提供了error類型異常的處理
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 對(duì)下執(zhí)行結(jié)果進(jìn)行處理,包括視圖的處理和異常的處理
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            // 鏈?zhǔn)綀?zhí)行攔截器鏈的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            // 攔截error類型異常,攔截后鏈?zhǔn)綀?zhí)行攔截器鏈的afterCompletion方法
            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);
                }
            }
        }
    }

看完之后,是不是或多或少都有一些想法了呢?不著急,我們先把一些前置知識(shí)給大家梳理一下。
在Spring運(yùn)行開(kāi)始,首先會(huì)初始化一些handleMapping,HandlerAdapter等核心組件,這些組件的作用,說(shuō)白了就是一點(diǎn)——將用戶發(fā)起的URL請(qǐng)求映射到我們寫好的controller方法,執(zhí)行業(yè)務(wù)邏輯;mvc將其封裝好供我們直接使用;

總結(jié):通過(guò)handleMapping,我們可以順利找到相關(guān)的適配器,再通過(guò)適配器調(diào)用handle()方法(我們的業(yè)務(wù)邏輯),得到result之后封裝成ModelAndView對(duì)象返回,從代碼中我們可以看到,getHandler()方法會(huì)返回一個(gè)HandlerExecutionChain對(duì)象,(這個(gè)執(zhí)行鏈對(duì)象包含兩個(gè)攔截器和一個(gè)處理器,攔截器分別做前置和后置處理)所以在代碼中有mappedHandler.getHandler()方法先拿到這個(gè)處理器,然后再通過(guò)getHandlerAdapter()獲得對(duì)應(yīng)的適配器緊接著執(zhí)行handle()方法處理業(yè)務(wù)邏輯,最后封裝ModelAndView對(duì)象返回。

大致步驟是說(shuō)完了,其中還是有一些細(xì)節(jié)我們沒(méi)有說(shuō)的到
比如說(shuō)初始化時(shí)是如何加載這些核心組件的,(可以思考一下url與處理邏輯如何映射?使用什么數(shù)據(jù)結(jié)構(gòu)?)
或者是為什么采用執(zhí)行鏈和適配器這種模式?
再或者說(shuō)ModelAndView對(duì)象是如何封裝的,以及mvc是如何處理異常的?
這些問(wèn)題就留給讀者在源碼中尋找答案了!此時(shí)只提供一個(gè)總體的思路框架......

面試總結(jié)系列第三面——?dú)g迎留言討論,共同進(jìn)步!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容