<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpingBoot的5個擴(kuò)展點(diǎn),超級實(shí)用!

          共 3603字,需瀏覽 8分鐘

           ·

          2024-04-12 04:45


          目錄
          • 1、初始化器ApplicationContextInitializer
          • 2、監(jiān)聽器ApplicationListener
          • 3、Runner
          • 4、BeanFactoryPostProcessor
          • 5、BeanPostProcessor
          • 6、后記


          1、初始化器ApplicationContextInitializer

          我們在啟動Spring Boot項(xiàng)目的時候,是執(zhí)行這樣一個方法來啟動的

          我們一層一層往下點(diǎn),最終發(fā)現(xiàn)執(zhí)行的是這個方法

          所以我們在啟動項(xiàng)目的時候也可以這樣啟動 new SpringApplication(SpringbootExtensionPointApplication.class).run(args); 老的只是包裝了一個靜態(tài)方法,實(shí)際底層就是實(shí)例化一個SpringApplication對象,然后調(diào)用它的run方法。

          我們進(jìn)到構(gòu)造方法里看下,紅框里面標(biāo)出來的,一個是設(shè)置初始化器,一個是設(shè)置監(jiān)聽器。

          點(diǎn)進(jìn)去看下,這兩個底層調(diào)的方法是一樣,就是傳入一個類型,通過Spring SPI的方式查找這個類型的實(shí)現(xiàn)類

          打個斷點(diǎn),啟動一下,此時有7個上下文初始器,這是系統(tǒng)自帶的,配置在不同的spring.factories文件中。

          現(xiàn)在我要新建一個自己的初始化器

          此時為了能夠讓Spring Boot在啟動的時候能夠掃描到我創(chuàng)建的初始化器應(yīng)該怎么辦?就是在spring.factories文件中添加一下,注冊一下,這樣就能掃描到,這個就是SPI。SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。

          那么這時候我們再啟動一下Spring Boot,發(fā)現(xiàn)自己創(chuàng)建的ApplicationContextInitializer也已經(jīng)注冊上來了,變成8個了。

          把斷點(diǎn)放掉,在控制臺中也打印出了這句話,那么這個就是第一個擴(kuò)展點(diǎn)ApplicationContextInitializer

          定義了這8個初始化器,那一定是有地方在調(diào)它們的,不然怎么會打印出來呢,那具體在什么地方調(diào)的,我們在自己的初始化器的地方打斷點(diǎn),看到已經(jīng)進(jìn)來了,然后看下方的堆棧信息,這個就是調(diào)用的地方。

          原來是調(diào)用了run()方法中的prepareContext()方法中的applyInitializers()方法,在這個方法中for循環(huán)的調(diào)用各個初始化器的initialize()方法,從而我們就能看到把Jack的ApplicationContextInitializer這句話給打印出來了。

          那么這個查找的方法就是以結(jié)果為導(dǎo)向來反查調(diào)用方,因?yàn)槟阏榈脑捠呛茈y找到,很難記住的,這個方法希望大家學(xué)習(xí)到。

          那么最后來看下我們第一個擴(kuò)展點(diǎn)所處的位置

          初始化器可以做一些事情,比如Environment對象設(shè)置一些變量配置。

          2、監(jiān)聽器ApplicationListener

          在上面構(gòu)造函數(shù)里我們發(fā)現(xiàn)除了有setInitializers,還有setListeners,那么這個listeners其實(shí)也是一個擴(kuò)展點(diǎn)。

          那么什么是監(jiān)聽器,就是這樣的,這個其實(shí)就是觀察者模式,ApplicationEventMulticaster發(fā)布事件,各個Listener監(jiān)聽事件。

          和初始化器一樣,現(xiàn)在我們自定義兩個監(jiān)聽器,一個是Starting,一個是Started,括號里面的是泛型,這個是一定要寫的,如果不寫的話就是不管什么類型的Event都會監(jiān)聽。

          這個泛型是上限為ApplicationEvent類型的Event,具體的實(shí)現(xiàn)類有很多個,Starting和Started只是其中兩個。

          現(xiàn)在我們還是把這兩個監(jiān)聽器通過SPI的方式加到配置中去

          好,運(yùn)行一下,我們看到這兩句話已經(jīng)打印出來了

          和監(jiān)聽器一樣,既然能夠打印出來,那肯定是有地方在調(diào)用,所以我們在JackStartingApplicationListener打個斷點(diǎn),然后看下堆棧信息

          我們可以看到在SpringApplication run()方法的listeners.starting()開始進(jìn)去發(fā)送ApplicationStartingEvent廣播事件,最后發(fā)布出去,由我們自己編寫的事件監(jiān)聽器接收到。

          那么ApplicationStartedEvent事件也是一樣的道理,通過打斷點(diǎn)的方式來找到它的調(diào)用方,最后我們再來看下此時的擴(kuò)展點(diǎn)圖

          3、Runner

          我們看到在listeners.started()后面有個callRunners

          我們點(diǎn)進(jìn)去看下,它其實(shí)就是從容器中獲取兩種類型的Runner,一種是ApplicationRunner,還有一種是CommandLineRunner,然后for循環(huán)的對它們進(jìn)行調(diào)用,那么其實(shí)這個也是一個擴(kuò)展點(diǎn)

          我們來寫一個自己的Runner

          運(yùn)行一下,看下打印出來了

          那么這個Runner的一般應(yīng)用場景就是資源釋放清理或者做注冊中心,因?yàn)閳?zhí)行到Runner的時候項(xiàng)目已經(jīng)啟動完畢了,這時候就可以注冊到注冊中心上去了。此時我們再看下擴(kuò)展點(diǎn)圖。

          4、BeanFactoryPostProcessor

          我們看下run方法里的refreshContext()方法

          這個方法底層會調(diào)spring里面的refresh()方法,這個方法里面就會做對容器的初始化。紅框里的invokeBeanFactoryPostProcessors()方法,這里也有一個擴(kuò)展點(diǎn),就是BeanFactoryPostProcessor,執(zhí)行對BeanFactory的后置處理。Spring Boot解析配置成BeanDefinition的操作也是在此方法中。

          現(xiàn)在我們來創(chuàng)建一個自己的BeanFactoryPostProcessor,這個方法里面可以修改beanFactory的屬性,beanfactory里面有BeanDefinition,可以修改BeanDefinition里面的值。BeanDefinition是一個bean的元數(shù)據(jù)的信息,有多少個bean就有多少個BeanDefinition

          運(yùn)行一下,也打印出來了

          此時我們再看下擴(kuò)展點(diǎn)圖,越來越完善了。

          5、BeanPostProcessor

          最后介紹的是BeanPostProcessor,它在通過反射構(gòu)造函數(shù)進(jìn)行bean實(shí)例化之后執(zhí)行,那么紅框里面標(biāo)出來的registerBeanPostProcessors()方法就是向BeanFactory中注冊beanpostprocessor,用于后續(xù)bean創(chuàng)建的攔截操作。

          現(xiàn)在我們來創(chuàng)建一個自己的BeanPostProcessor,一共有兩個方法,postProcessBeforeInitializationpostProcessAfterInitialization,不過我們一般用postProcessAfterInitialization,在bean調(diào)用反射構(gòu)造函數(shù)實(shí)例化之后執(zhí)行。著名的應(yīng)用場景AOP底層就是通過BeanPostProcessor來實(shí)現(xiàn)的。

          現(xiàn)在我在postProcessAfterInitialization上打個斷點(diǎn),看下堆棧信息是在哪里調(diào)用的

          是在finishBeanFactoryInitialization()方法處調(diào)用的

          6、后記

          最后我來把擴(kuò)展點(diǎn)圖補(bǔ)充完整,如下所示,很清晰明了,在什么時候調(diào)用了什么,我們自己開發(fā)的時候結(jié)合應(yīng)用場景,在什么時候要干什么事,就知道要創(chuàng)建什么類型的擴(kuò)展點(diǎn)了。

          本文前三個講的是Spring Boot里面自己有的擴(kuò)展點(diǎn),后兩個因?yàn)镾pring Boot底層調(diào)的是Spring的源碼,所以屬于Spring里面的擴(kuò)展點(diǎn),所以如果這么算的話Spring里面的擴(kuò)展點(diǎn)還有很多擴(kuò)展點(diǎn),比如InitializeBean、Aware等等這里都沒講,等待大家去發(fā)掘 ~

          瀏覽 57
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  手机免费在线看片网址av | 天堂综合久久 | 天堂中文资源在线 | 青草青视频在线 | 免费毛片18女人毛片大全在线看 |