廣州企業(yè)網(wǎng)站建設(shè)公司bt磁力搜索神器
上一篇《【Spring6源碼?MVC】初始化registry,完成url和controller的映射關(guān)系》我們知道,在IOC容器加載的同時,初始化了registry這個HashMap,這個HashMap中存放了請求路徑和對應(yīng)的方法。當我們請求進來,會通過這個registry去獲取對應(yīng)的方法。
在SpringBoot項目啟動的時候,會加載一個線程:
步入run方法:
步入run方法:
當前這個類是Worker
:Class Worker 主要維護運行任務(wù)的線程的中斷控制狀態(tài),以及其他次要簿記。此類適時地擴展了 AbstractQueuedSyncer,以簡化獲取和釋放圍繞每個任務(wù)執(zhí)行的鎖。這可以防止旨在喚醒等待任務(wù)的工作線程而不是中斷正在運行的任務(wù)的中斷。我們實現(xiàn)了一個簡單的非重入互斥鎖,而不是使用 ReentrantLock,因為我們不希望工作線程任務(wù)在調(diào)用池控制方法(如 setCorePoolSize)時能夠重新獲取鎖。此外,為了在線程實際開始運行任務(wù)之前抑制中斷,我們將鎖定狀態(tài)初始化為負值,并在啟動時清除它(在 runWorker 中)。
這個run方法主要是啟動主運行循環(huán),以此從隊列中取出task執(zhí)行。
當我們發(fā)送一個請求時:http://localhost:8081/user/test
會調(diào)用這個runWorker方法中的task.run()
:
runWorker這個方法是主工作線程運行循環(huán)。反復從隊列中獲取任務(wù)并執(zhí)行它們,同時處理許多問題:
- 我們可以從初始任務(wù)開始,在這種情況下,我們不需要獲取第一個任務(wù)。否則,只要池正在運行,我們就從getTask獲取任務(wù)。如果它返回 null,則工作線程由于池狀態(tài)或配置參數(shù)更改而退出。其他退出是由外部代碼中的異常拋出引起的,在這種情況下,complete突然持有,這通常會導致processWorkerExit替換此線程。
- 在運行任何任務(wù)之前,獲取鎖以防止在任務(wù)執(zhí)行時出現(xiàn)其他池中斷,然后我們確保除非池停止,否則該線程沒有設(shè)置中斷。
- 每次任務(wù)運行之前都會調(diào)用 beforeExecute,這可能會引發(fā)異常,在這種情況下,我們會導致線程死亡(以 completeA 突然為 true 中斷循環(huán))而不處理任務(wù)。
- 假設(shè) beforeExecute 正常完成,我們運行任務(wù),收集其拋出的任何異常以發(fā)送到 afterExecute。我們分別處理 RuntimeException、Error(規(guī)范保證我們捕獲這兩個錯誤)和任意 Throwable。因為我們無法在 Runnable.run 中重新拋出 Throwable,所以我們在出路時將它們包裝在 Errors 中(到線程的 UncaughtExceptionHandler)。任何拋出的異常也保守地導致線程死亡。
- task.run 完成后,我們調(diào)用 afterExecute,這也可能會拋出異常,這也會導致線程死亡。根據(jù) JLS Sec 14.20,即使 task.run 拋出,此異常也會生效。異常機制的凈效果是,afterExecute 和線程的 UncaughtExceptionHandler 具有盡可能準確的信息,我們可以提供有關(guān)用戶代碼遇到的任何問題的信息。
當我們發(fā)送http://localhost:8081/user/test
請求時,步入這個task.run()
方法中:
步入doRun方法中:
…
…
…
最終經(jīng)過數(shù)十個調(diào)用,會在一個filterChanin鏈中調(diào)用servlet.service(request, response)
方法:
然后會判斷當前請求中的方法是否包含規(guī)定的方法:HTTP_SERVLET_METHODS.contains(request.getMethod())
然后會調(diào)用doGet方法:
之后調(diào)用經(jīng)典的doService方法:
步入doService方法之后,又會調(diào)用doDispatch方法:
看一看這個核心方法:doDispatch。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {...try {...try {// 檢查是否文件請求processedRequest = checkMultipart(request);...// 根據(jù)請求找到對應(yīng)的控制器執(zhí)行器鏈HandlerExecutionChain mappedHandler = getHandler(processedRequest);...// 找到對應(yīng)控制器的適配器,用來執(zhí)行操作的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());...// 執(zhí)行攔截器前置if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 真正執(zhí)行控制器邏輯mv = ha.handle(processedRequest, response, mappedHandler.getHandler());...// 執(zhí)行攔截器后置mappedHandler.applyPostHandle(processedRequest, response, mv);}...// 處理返回結(jié)果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}...
}
先看checkMultipart(request)
方法,它是用來檢查是否是文件上傳,如果有文件上傳,則將request包裝成StandardMultipartHttpServletRequest
關(guān)于mappedHandler = getHandler(processedRequest);
和HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
這兩個方法,和前面AOP講到的獲取攔截器和適配器的邏輯差不多,循環(huán)去匹配,這里就不展開了。
重點看一下mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
這個真正執(zhí)行控制器邏輯的方法。
步入handle方法:最終來到這:
步入invokeHandlerMethod方法:
首先創(chuàng)建ServletInvocableHandlerMethod對象,再去調(diào)用。
步入invokeAndHandle方法:
首先去執(zhí)行控制器的邏輯,如果有結(jié)果再去處理結(jié)果。
看看如何執(zhí)行控制器的邏輯的,步入invokeForRequest方法:
首先獲取方法參數(shù),再去invoke:
如何實現(xiàn)的就不看了吧,最后肯定是調(diào)用本地方法去執(zhí)行控制器的邏輯。
感興趣可以翻一翻我的博客,有一篇是如何解析本地方法的??梢钥纯碿pp是如何實現(xiàn)的。
最后,我們能得到返回的結(jié)果:
最后會去調(diào)用這行代碼:
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
步入handleReturnValue方法:
步入handleReturnValue方法:
步入writeWithMessageConverters方法:
最后經(jīng)過層層包裝,調(diào)用((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
方法:
步入write方法:
步入writeInternal方法:
步入copy方法:
落葉歸根,結(jié)束。
其實關(guān)于SpringMVC還有很多沒寫,但是首先知道這個流程就基本夠用了,之后用到哪些的哪些的時候,再按著這個流程看就好。
值得一提的是我們這個demo沒有文件,也沒有參數(shù),所以擼流程還是很容易的,如果有參數(shù)還要注意是如何解析參數(shù)的,如果用@RequestParam注解的話,直接通過反射就可以獲取到,Spring也提供了處理這個注解的解析器,如果不加注解,會默認使用名稱綁定,底層用asm框架讀取字節(jié)碼來獲取參數(shù)名稱,所以編碼記得用@RequestParam聲明參數(shù),之后會放進一個緩存數(shù)組中,在ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
代碼執(zhí)行時,就會封裝成ServletInvocableHandlerMethod 對象。
其他的暫且先不提了,都比較簡單,點點看看就行了。