
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ò)程,話不多少,直接上圖!

具體流程: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í)緩存!看圖

從上面的流程可以知道,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.交互流程

總結(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)步!