自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java進(jìn)階篇——Springboot2源碼探究

開發(fā) 前端
看起來是將元數(shù)據(jù)中的包名提取成數(shù)組,我們打開看看該類在初始化的時(shí)候具體干了什么,可以看到實(shí)際上在初始化時(shí)調(diào)用了一個(gè)ClassUtils.getPackageName, 傳入了一個(gè)什么呢,metadata.getclassname,他是什么呢,打斷點(diǎn)!

1.@EnableAutoConfiguration

除了元注解之外,EnableAutoConfiguration包含了兩大重要部分:

1)@AutoConfigurationPackage注解

該注解只導(dǎo)入了一個(gè)內(nèi)部類:AutoConfigurationPackages.Registrar.class

類中有兩個(gè)方法

從名字上看,registerBeanDefinitions方法注冊了定義好的一些Bean,determineImports方法決定這些要不要導(dǎo)入

registerBeanDefinitions調(diào)用了register方法,并傳入了registry參數(shù)和一個(gè)元數(shù)據(jù)名字?jǐn)?shù)組。registry參數(shù)是一個(gè)接口,
實(shí)際場景中必然是使用的實(shí)現(xiàn)類,可以在該方法打斷點(diǎn)debug

發(fā)現(xiàn)實(shí)際上他的實(shí)現(xiàn)類是一個(gè)名為DefaultListableBeanFactory,并且可以清晰的看到該類的一些基本屬性的值

可以看到,registry中存儲(chǔ)的是一些關(guān)于項(xiàng)目程序的基本配置和bean實(shí)例名。比如一些網(wǎng)頁支持組件和springContext以及一些加載器

還有用戶自定義的類,這些類組件中存儲(chǔ)了bean名字、作用域、懶加載等等

再來看另一個(gè)參數(shù):new PackageImports(metadata).getPackageNames().toArray(new String[0])

看起來是將元數(shù)據(jù)中的包名提取成數(shù)組,我們打開看看該類在初始化的時(shí)候具體干了什么,可以看到實(shí)際上在初始化時(shí)調(diào)用了一個(gè)ClassUtils.getPackageName,
傳入了一個(gè)什么呢,metadata.getclassname,他是什么呢,打斷點(diǎn)!

,實(shí)際上就是啟動(dòng)類的全名,com.***.application,而這個(gè)方法則是將classname的前綴提取出來,即提取出我們的包名com.&&&

即這個(gè)AutoConfigurationPackages.register方法傳入了我們的一大堆初始bean的名字、配置和總包名com.**,我們研究研究他做了什么

首先是判斷定義的bean中有沒有Bean,這個(gè)bean是類的屬性,存著當(dāng)前類的全名:org.springframework.boot.autoconfigure.AutoConfigurationPackages

大概想處理的是用戶自定義了AutoConfigurationPackages類的情況。當(dāng)前是沒有定義的,所以直接走else邏輯

new了一個(gè)GenericBeanDefinition,暫且叫他通用的bean定義工具,set一大堆東西。之后調(diào)用了注冊方法。把工具丟了進(jìn)去。

這個(gè)注冊方法里大概是將AutoConfigurationPackages類同樣注冊進(jìn)了定義bean的map中,然后將map中的所有值加入到了這個(gè)默認(rèn)的bean工廠中,之后這個(gè)類就走完了。

因此該注解的作用即是將自定義的bean以及一些基本類型、原始組件注冊到bean工廠中

2)AutoConfigurationImportSelector類

該類中點(diǎn)進(jìn)去映入眼簾的就是selectImports方法。聽起來名字是選擇導(dǎo)入,也就是該方法決定了要加載什么依賴組件

而該方法調(diào)用了并且只調(diào)用了getAutoConfigurationEntry方法來獲取要加載的組件。

我們來看看這個(gè)方法,先是調(diào)用getAttributes獲取了某個(gè)東西,打斷點(diǎn)發(fā)現(xiàn)是

這樣就很熟悉了,是EnableAutoConfiguration注解的兩個(gè)屬性值,雖然默認(rèn)值為null。點(diǎn)進(jìn)方法體發(fā)現(xiàn)確實(shí)是這樣。

getCandidateConfigurations(annotationMetadata, attributes)方法調(diào)用了一大串,最終調(diào)用了這個(gè)方法loadSpringFactories,
該方法的大致內(nèi)容是,從當(dāng)前所有的依賴包中加載META-INF/目錄下的spring.factories文件中尋找一些組件。
可以看到他先嘗試從緩存中拿,如果為空,則去依賴包中的META-INF/目錄下的spring.factories中加載。

拿到這個(gè)組件列表之后,還要進(jìn)行一層過濾,抽取含有factoryTypeName的組件列表。
這個(gè)name即是org.springframework.boot.autoconfigure.EnableAutoConfiguration自動(dòng)配置注解

再回到getEntry方法,之后對(duì)獲取到的組件列表進(jìn)行去重,然后試圖從列表中拿出排除項(xiàng),
也就是attributes中獲取到的EnableAutoConfiguration注解的兩個(gè)屬性值的內(nèi)容。很容易理解,屬性值中配置了要排除的內(nèi)容將在這里進(jìn)行排除。

而后調(diào)用filter方法對(duì)列表進(jìn)行篩選,而篩選使用的是autoConfigurationMetadata這個(gè)類,
由此可見,這個(gè)類是某種篩選規(guī)則,它里面存儲(chǔ)了501個(gè)properties,所以篩選規(guī)則可能就是逐一比對(duì),篩選出Metadata中有的組件。

篩選出來的結(jié)果即是最終要加載的組件bean。

也就是說,@EnableAutoConfiguration的兩個(gè)重要成員,一個(gè)決定了要加載默認(rèn)的哪些組件(用戶自定義bean、數(shù)值包裝類、字符串類等等)
和配置,另一個(gè)決定了要加載哪些外部依賴類,即通過starter等通過pom引入的組件。

2.請求處理

如何知道spring是怎么、在哪處理請求的呢,有個(gè)很簡單的方法,在properties中將日志級(jí)別設(shè)置為debug,即dubug=true。而后運(yùn)行程序,發(fā)送任意一個(gè)請求

就會(huì)發(fā)現(xiàn)spring打印出了關(guān)于處理該請求的一些細(xì)節(jié),比如處理請求是從初始化DispatcherServlet開始的,
由此可見請求處理最重要的便是DispatcherServlet。而后AbstractHandlerMapping識(shí)別到了處理該請求的具體方法。

我們進(jìn)入到DispatcherServlet中,查看他的方法。

學(xué)過mvc原生web開發(fā)的都知道,servlet有兩大重要方法,doGet和doPost。而DispatcherServlet繼承了FrameworkServlet繼承了HttpServletBean,
HttpServletBean繼承了HttpServlet,由此可知,DispatcherServlet也是一個(gè)httpServlet。那么我們就有思路了,從他的do**方法開始探究。

1)doDispatch

從名字看,這個(gè)方法似乎是為了做轉(zhuǎn)發(fā)。并且他的參數(shù)幾乎和doGet方法是一模一樣的

最開始是初始化了一大堆東西,包括處理異步請求的異步管理器以及檢查是否是文件上傳的請求巴拉巴拉,而后這個(gè)getHandler方法,直接獲取到了處理這個(gè)請求的具體方法。

它是如何處理的呢,他遍歷了一個(gè)handlerMappings的集合,這個(gè)mapping里面存儲(chǔ)的是spring一些專處理映射的類

,比如歡迎頁的映射,以及我們使用requestMapping標(biāo)注的url。這樣就打通了,他會(huì)在requestMappingHandlerMapping中查找到/hello請求并且找到他映射的方法。
具體查找調(diào)用了HandlerMapping的gethandler,由此就和日志中的對(duì)上了,gethandler方法中就是根據(jù)url在映射中找方法,先拋開這個(gè)細(xì)節(jié)不看。

拿到這個(gè)處理器(方法)后,將其丟入到了HandlerAdapter請求適配器中,這個(gè)在mvc架構(gòu)中熟悉的身影。
之后并不是立即執(zhí)行該方法而是先判斷方法的種類,如果是get或者h(yuǎn)ead方法,則執(zhí)行邏輯。

這個(gè)邏輯會(huì)衡返回一個(gè)-1,也就是寫死的,我不理解為什么是這樣,在網(wǎng)上搜了Last-Modified,發(fā)現(xiàn)這是一種緩存機(jī)制,
這也就理解了為什么必須是get方法,而他實(shí)現(xiàn)需要實(shí)現(xiàn)LastModified接口,我并沒有實(shí)現(xiàn)這個(gè),所以spring這個(gè)判斷邏輯會(huì)衡false。

之后是一個(gè)applyPreHandle的方法,點(diǎn)進(jìn)去就會(huì)發(fā)現(xiàn)是使用當(dāng)前spring的攔截器組件對(duì)請求進(jìn)行攔截,

能發(fā)現(xiàn)就是一些請求方法攔截器、token攔截器、資源攔截器等等,也就是一些壞的請求會(huì)在這條被攔截

之后就是請求適配器執(zhí)行自己的處理邏輯了,可以看到他返回的是一個(gè)modelAndView類型的實(shí)例,那么就意味著,此時(shí)方法已經(jīng)被執(zhí)行了。

但是實(shí)際上并沒有使用modelAndView作為返回值,而是直接返回的string。所以mv是一個(gè)空值,但是可以在響應(yīng)體中觀察到,
已經(jīng)有19字節(jié)的東西被寫進(jìn)了響應(yīng)體中,頁印證了方法已經(jīng)被執(zhí)行。

緊接著是判斷請求是否是異步請求,如果是異步請求可能會(huì)做First響應(yīng)之類的處理。

再往下是一個(gè)名叫應(yīng)用默認(rèn)的視圖名的方法,將請求體中或者默認(rèn)的視圖名加入model中。

因?yàn)槿绻麘?yīng)用了modelandview,此時(shí)才只是一個(gè)model,必然要添加對(duì)應(yīng)的view??梢院唵螠y試一下。我們在處理方法中new一個(gè)modelandview對(duì)象,設(shè)置一個(gè)model值并返回。

可以看到,經(jīng)過此方法之后,我們并沒有設(shè)置view值,系統(tǒng)默認(rèn)將hello當(dāng)作了view加入model,這個(gè)默認(rèn)值即是請求路徑去掉前面的/。

這個(gè)方法之后,又是一個(gè)類似于攔截器,有點(diǎn)類似于在方法執(zhí)行前后各執(zhí)行一次攔截。攔截器執(zhí)行完畢后,方法結(jié)束。
后面就是一些兜底處理,比如如果是文件相關(guān)之類的請求,要關(guān)閉對(duì)應(yīng)的流。如果是異步請求執(zhí)行怎樣的邏輯。

2)handle方法

我們知道,適配器的handle方法里面執(zhí)行了方法邏輯,具體是怎么執(zhí)行的呢。實(shí)際上是調(diào)用了super的AbstractHandlerMethodAdapter的handle方法。
這個(gè)handle方法又會(huì)調(diào)用自己(RequestMappingHandlerAdapter)的handleInternal方法。該方法內(nèi)對(duì)session做了一些處理,
而后調(diào)用invokeHandlerMethod方法,這個(gè)方法內(nèi)做了很多的處理。比如WebDataBinderFactory binderFactory對(duì)象,他是對(duì)參數(shù)之中的數(shù)據(jù)格式做轉(zhuǎn)換的,他里面初始化了128種對(duì)象轉(zhuǎn)換的方式;

又或者初始化一些參數(shù)解析器和返回值處理器:里面對(duì)各種參數(shù)和返回值做對(duì)應(yīng)的解析和處理,總之就是把這些丟入到一個(gè)mavContainer容器中。

然后調(diào)用invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0])方法,同時(shí)傳入的還有webRequest,
這個(gè)就是將請求體和響應(yīng)體包裝到了一起。這個(gè)方法里面第一行就直接執(zhí)行了方法,可以看到此時(shí)已經(jīng)有返回值了。

這個(gè)方法就比較簡單了:獲取參數(shù)、執(zhí)行方法

①getMethodArgumentValues

該方法最開始是獲取映射方法的參數(shù)類型以及參數(shù)名

之后做的事大概可以猜到,就是從請求中找出這些參數(shù)名對(duì)應(yīng)的參數(shù)并且轉(zhuǎn)化成對(duì)應(yīng)的類型。可以看看具體的

首先定義了一個(gè)接收數(shù)組,用來存儲(chǔ)獲取到的參數(shù),而后嘗試從providedArgs中拿對(duì)應(yīng)的參數(shù),但實(shí)際上這個(gè)參數(shù)傳的是空值。

所以執(zhí)行后面的邏輯,從請求中拿,并且同時(shí)傳入了mav容器,這個(gè)容器中就有格式轉(zhuǎn)換器和返回值處理器等。

該方法里面有兩個(gè)重要的構(gòu)成,getArgumentResolver獲取參數(shù)解析器和resolveArgument解析參數(shù)。獲取參數(shù)解析器方法中,
循環(huán)目前已經(jīng)存進(jìn)ioc容器的解析器組件,和參數(shù)進(jìn)行一一比對(duì),找到可以解析對(duì)應(yīng)參數(shù)的解析器。

解析參數(shù)方法中,就是獲取參數(shù)名容器→獲取方法參數(shù)容器→獲取參數(shù)名→解析參數(shù)。之后會(huì)做很多的后續(xù)處理,比如格式轉(zhuǎn)換之類的。

②doInvoke

這個(gè)方法比較簡單,利用之前已經(jīng)存入InvocableHandlerMethod中的反射方法public java.lang.String
com.glodon.controller.HelloController.home(java.lang.String),實(shí)際上,該對(duì)象實(shí)例也是專門存儲(chǔ)方法處理的相關(guān)組件的。
獲取到方法后,利用spring反射機(jī)制執(zhí)行該方法。

3)請求響應(yīng)

前面將方法執(zhí)行完之后,封裝完modelAndView,在執(zhí)行完處理后攔截器后,還要執(zhí)行一個(gè)兜底方法對(duì)結(jié)果進(jìn)行處理,從名字來看,他是用來處理結(jié)果轉(zhuǎn)發(fā)的。

開始是判斷方法執(zhí)行是否有異常,如果有則走異常處理邏輯。而后如果modelandview不為空,則執(zhí)行一個(gè)render方法render(mv, request, response)

render方法首先從請求中拿到語言標(biāo)識(shí),并加入到響應(yīng)體中,而后拿出modelandview中的視圖名,即重定向或者其他視圖。
然后調(diào)用resolveViewName方法對(duì)視圖名進(jìn)行處理,對(duì)當(dāng)前容器中的所有解析器進(jìn)行遍歷,哪個(gè)可以解析這個(gè)視圖名,就直接返回解析結(jié)果。

解析器的解析過程,以ContentNegotiatingViewResolver舉例。

這個(gè)解析器獲取了請求的Attributes,然后傳入getMediaTypes方法中并調(diào)用來獲取返回?cái)?shù)據(jù)類型。

getMediaTypes里面其實(shí)就是一個(gè)雙重循環(huán)匹配,格式化網(wǎng)頁請求→獲取瀏覽器請求頭中的可接受媒體類型→獲取系統(tǒng)可生產(chǎn)的媒體類型→初始化匹配的媒體類型。
然后進(jìn)行一個(gè)雙重循環(huán),如果匹配成功,則把對(duì)應(yīng)的媒體類型加入到compatibleMediaTypes中。

排序完成后就是一個(gè)set→List,然后進(jìn)行一個(gè)排序,排序的依據(jù)就是媒體類型的權(quán)重。

瀏覽器在發(fā)送請求的時(shí)候會(huì)給服務(wù)器一個(gè)accept,里面明確表示了瀏覽器可以接收的返回類型以及他的權(quán)重,而這里的排序就是按照這個(gè)權(quán)重進(jìn)行排序的。

該方法返回后,在接著看解析方法,之后調(diào)用了getCandidateViews獲取候選視圖。

getCandidateViews方法內(nèi)同樣是個(gè)雙層循環(huán)。外層是除了當(dāng)前解析器外的其他三個(gè)視圖解析器,內(nèi)層是對(duì)匹配到的媒體類型。

通過調(diào)試,發(fā)現(xiàn)最終是被InternalResourceViewResolver成功處理,我們只看他的細(xì)節(jié)。

同樣還是resolveViewname方法,該方法先嘗試去緩存中拿,拿不到了才執(zhí)行createView方法創(chuàng)建視圖。而這個(gè)方法就很清晰明了了

判斷是重定向還是轉(zhuǎn)發(fā)來生成對(duì)應(yīng)的視圖。最終返回合適的視圖,添加緩存巴拉巴拉。

我們測試的剛好是一個(gè)重定向視圖,所以返回的結(jié)果就是bean為redirect,url為/helloWorld的視圖。?

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2022-09-13 09:02:19

SpringBootStarter機(jī)制

2017-09-19 15:01:06

PHP漏洞滲透測試

2023-03-26 09:08:36

2024-05-08 08:59:02

2015-10-20 15:57:48

ReactiveCociOS

2023-07-10 07:22:16

2021-01-01 09:20:20

操作DjangoORM

2023-08-29 10:27:32

2011-10-24 09:49:37

管理非限制進(jìn)程SELinux

2017-09-19 15:45:39

2010-07-05 13:08:42

用Visio畫UML圖

2021-10-27 16:52:37

LayoutInfl源碼解析

2021-09-12 07:30:10

配置

2021-05-17 09:50:06

Kubebuilde源碼CURD

2021-04-26 09:25:10

JavaKafka架構(gòu)

2022-10-27 10:32:09

Presto SQLJoin大數(shù)據(jù)

2021-10-29 08:19:54

JMeterJava java sample

2020-09-18 09:15:22

數(shù)據(jù)庫Sqlite3進(jìn)階

2017-09-12 10:26:47

springbootmaven結(jié)構(gòu)

2021-01-19 15:59:14

程序員算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)