1、初始化器ApplicationContextInitializer
我們?cè)趩?dòng)Spring Boot項(xiàng)目的時(shí)候,是執(zhí)行這樣一個(gè)方法來(lái)啟動(dòng)的
我們一層一層往下點(diǎn),最終發(fā)現(xiàn)執(zhí)行的是這個(gè)方法
所以我們?cè)趩?dòng)項(xiàng)目的時(shí)候也可以這樣啟動(dòng) new SpringApplication(SpringbootExtensionPointApplication.class).run(args); 老的只是包裝了一個(gè)靜態(tài)方法,實(shí)際底層就是實(shí)例化一個(gè)SpringApplication對(duì)象,然后調(diào)用它的run方法。
我們進(jìn)到構(gòu)造方法里看下,紅框里面標(biāo)出來(lái)的,一個(gè)是設(shè)置初始化器,一個(gè)是設(shè)置監(jiān)聽(tīng)器。
點(diǎn)進(jìn)去看下,這兩個(gè)底層調(diào)的方法是一樣,就是傳入一個(gè)類(lèi)型,通過(guò)Spring SPI的方式查找這個(gè)類(lèi)型的實(shí)現(xiàn)類(lèi)
打個(gè)斷點(diǎn),啟動(dòng)一下,此時(shí)有7個(gè)上下文初始器,這是系統(tǒng)自帶的,配置在不同的spring.factories文件中。
現(xiàn)在我要新建一個(gè)自己的初始化器
此時(shí)為了能夠讓Spring Boot在啟動(dòng)的時(shí)候能夠掃描到我創(chuàng)建的初始化器應(yīng)該怎么辦?就是在spring.factories文件中添加一下,注冊(cè)一下,這樣就能掃描到,這個(gè)就是SPI。SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。
那么這時(shí)候我們?cè)賳?dòng)一下Spring Boot,發(fā)現(xiàn)自己創(chuàng)建的ApplicationContextInitializer也已經(jīng)注冊(cè)上來(lái)了,變成8個(gè)了。
把斷點(diǎn)放掉,在控制臺(tái)中也打印出了這句話,那么這個(gè)就是第一個(gè)擴(kuò)展點(diǎn)ApplicationContextInitializer
定義了這8個(gè)初始化器,那一定是有地方在調(diào)它們的,不然怎么會(huì)打印出來(lái)呢,那具體在什么地方調(diào)的,我們?cè)谧约旱某跏蓟鞯牡胤酱驍帱c(diǎn),看到已經(jīng)進(jìn)來(lái)了,然后看下方的堆棧信息,這個(gè)就是調(diào)用的地方。
原來(lái)是調(diào)用了run()方法中的prepareContext()方法中的applyInitializers()方法,在這個(gè)方法中for循環(huán)的調(diào)用各個(gè)初始化器的initialize()方法,從而我們就能看到把Jack的ApplicationContextInitializer這句話給打印出來(lái)了。
那么這個(gè)查找的方法就是以結(jié)果為導(dǎo)向來(lái)反查調(diào)用方,因?yàn)槟阏榈脑捠呛茈y找到,很難記住的,這個(gè)方法希望大家學(xué)習(xí)到。
那么最后來(lái)看下我們第一個(gè)擴(kuò)展點(diǎn)所處的位置
初始化器可以做一些事情,比如Environment對(duì)象設(shè)置一些變量配置。
2、監(jiān)聽(tīng)器ApplicationListener
在上面構(gòu)造函數(shù)里我們發(fā)現(xiàn)除了有setInitializers,還有setListeners,那么這個(gè)listeners其實(shí)也是一個(gè)擴(kuò)展點(diǎn)。
那么什么是監(jiān)聽(tīng)器,就是這樣的,這個(gè)其實(shí)就是觀察者模式,ApplicationEventMulticaster發(fā)布事件,各個(gè)Listener監(jiān)聽(tīng)事件。
和初始化器一樣,現(xiàn)在我們自定義兩個(gè)監(jiān)聽(tīng)器,一個(gè)是Starting,一個(gè)是Started,括號(hào)里面的是泛型,這個(gè)是一定要寫(xiě)的,如果不寫(xiě)的話就是不管什么類(lèi)型的Event都會(huì)監(jiān)聽(tīng)。
這個(gè)泛型是上限為ApplicationEvent類(lèi)型的Event,具體的實(shí)現(xiàn)類(lèi)有很多個(gè),Starting和Started只是其中兩個(gè)。
現(xiàn)在我們還是把這兩個(gè)監(jiān)聽(tīng)器通過(guò)SPI的方式加到配置中去
好,運(yùn)行一下,我們看到這兩句話已經(jīng)打印出來(lái)了
和監(jiān)聽(tīng)器一樣,既然能夠打印出來(lái),那肯定是有地方在調(diào)用,所以我們?cè)贘ackStartingApplicationListener打個(gè)斷點(diǎn),然后看下堆棧信息
我們可以看到在SpringApplication run()方法的listeners.starting()開(kāi)始進(jìn)去發(fā)送ApplicationStartingEvent廣播事件,最后發(fā)布出去,由我們自己編寫(xiě)的事件監(jiān)聽(tīng)器接收到。
那么ApplicationStartedEvent事件也是一樣的道理,通過(guò)打斷點(diǎn)的方式來(lái)找到它的調(diào)用方,最后我們?cè)賮?lái)看下此時(shí)的擴(kuò)展點(diǎn)圖
3、Runner
我們看到在listeners.started()后面有個(gè)callRunners
我們點(diǎn)進(jìn)去看下,它其實(shí)就是從容器中獲取兩種類(lèi)型的Runner,一種是ApplicationRunner,還有一種是CommandLineRunner,然后for循環(huán)的對(duì)它們進(jìn)行調(diào)用,那么其實(shí)這個(gè)也是一個(gè)擴(kuò)展點(diǎn)
我們來(lái)寫(xiě)一個(gè)自己的Runner
運(yùn)行一下,看下打印出來(lái)了
那么這個(gè)Runner的一般應(yīng)用場(chǎng)景就是資源釋放清理或者做注冊(cè)中心,因?yàn)閳?zhí)行到Runner的時(shí)候項(xiàng)目已經(jīng)啟動(dòng)完畢了,這時(shí)候就可以注冊(cè)到注冊(cè)中心上去了。此時(shí)我們?cè)倏聪聰U(kuò)展點(diǎn)圖。
4、BeanFactoryPostProcessor
我們看下run方法里的refreshContext()方法
這個(gè)方法底層會(huì)調(diào)spring里面的refresh()方法,這個(gè)方法里面就會(huì)做對(duì)容器的初始化。紅框里的invokeBeanFactoryPostProcessors()方法,這里也有一個(gè)擴(kuò)展點(diǎn),就是BeanFactoryPostProcessor,執(zhí)行對(duì)BeanFactory的后置處理。Spring Boot解析配置成BeanDefinition的操作也是在此方法中。
現(xiàn)在我們來(lái)創(chuàng)建一個(gè)自己的BeanFactoryPostProcessor,這個(gè)方法里面可以修改beanFactory的屬性,beanfactory里面有BeanDefinition,可以修改BeanDefinition里面的值。BeanDefinition是一個(gè)bean的元數(shù)據(jù)的信息,有多少個(gè)bean就有多少個(gè)BeanDefinition。
運(yùn)行一下,也打印出來(lái)了
此時(shí)我們?cè)倏聪聰U(kuò)展點(diǎn)圖,越來(lái)越完善了。
5、BeanPostProcessor
最后介紹的是BeanPostProcessor,它在通過(guò)反射構(gòu)造函數(shù)進(jìn)行bean實(shí)例化之后執(zhí)行,那么紅框里面標(biāo)出來(lái)的registerBeanPostProcessors()方法就是向BeanFactory中注冊(cè)beanpostprocessor,用于后續(xù)bean創(chuàng)建的攔截操作。
現(xiàn)在我們來(lái)創(chuàng)建一個(gè)自己的BeanPostProcessor,一共有兩個(gè)方法,postProcessBeforeInitialization和postProcessAfterInitialization,不過(guò)我們一般用postProcessAfterInitialization,在bean調(diào)用反射構(gòu)造函數(shù)實(shí)例化之后執(zhí)行。著名的應(yīng)用場(chǎng)景AOP底層就是通過(guò)BeanPostProcessor來(lái)實(shí)現(xiàn)的。
現(xiàn)在我在postProcessAfterInitialization上打個(gè)斷點(diǎn),看下堆棧信息是在哪里調(diào)用的
是在finishBeanFactoryInitialization()方法處調(diào)用的
后記
最后我來(lái)把擴(kuò)展點(diǎn)圖補(bǔ)充完整,如下所示,很清晰明了,在什么時(shí)候調(diào)用了什么,我們自己開(kāi)發(fā)的時(shí)候結(jié)合應(yīng)用場(chǎng)景,在什么時(shí)候要干什么事,就知道要?jiǎng)?chuàng)建什么類(lèi)型的擴(kuò)展點(diǎn)了。
本文前三個(gè)講的是Spring Boot里面自己有的擴(kuò)展點(diǎn),后兩個(gè)因?yàn)镾pring Boot底層調(diào)的是Spring的源碼,所以屬于Spring里面的擴(kuò)展點(diǎn),所以如果這么算的話Spring里面的擴(kuò)展點(diǎn)還有很多擴(kuò)展點(diǎn),比如InitializeBean、Aware等等這里都沒(méi)講,等待大家去發(fā)掘,謝謝觀看 ~
審核編輯:劉清
-
AOP
+關(guān)注
關(guān)注
0文章
40瀏覽量
11145 -
for循環(huán)
+關(guān)注
關(guān)注
0文章
61瀏覽量
2583
原文標(biāo)題:SpingBoot的5個(gè)擴(kuò)展點(diǎn),超級(jí)實(shí)用!
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
超級(jí)電容電源模組
bW6101-超級(jí)法拉電容過(guò)壓保護(hù)芯片
超級(jí)電容保護(hù)芯片-BW6101
超級(jí)電容器充電
基于FPGA的超級(jí)電容均壓及充放電設(shè)計(jì)方案
超級(jí)電容容量選取
一個(gè)提供標(biāo)準(zhǔn)擴(kuò)展點(diǎn)的javascript小部件
模擬/數(shù)字點(diǎn)擴(kuò)展模塊
紫光5G超級(jí)SIM卡開(kāi)售,支持存儲(chǔ)功能和SIM通信功能
紫光與聯(lián)通的5G超級(jí)SIM卡可支持存儲(chǔ)功能
特斯拉11月已開(kāi)放517個(gè)超級(jí)充電樁
Arduino 32點(diǎn)IO擴(kuò)展板開(kāi)源項(xiàng)目

評(píng)論