前言
前一個小節,我們講解了一下SpringBoot的配置屬性"籃子"——EnvironmentPostProcessor,今天我們一起學習一下Springboot家族中另一個重要的元件——ApplicationContextInitializer,雖然這個元件的功能很強單,但是我們平時用的並不是很多,原因還是很簡單的,每一個元件應該有它自己擅長的領域,各個元件各司其職,不能越俎代庖,種別人家的地
spring家族元件分析系列教程
如果你以前看過或者沒有看到spring原始碼的,你一定都會聽說過BeanFactory或者ApplicationContext,本文並不是分析原始碼的教程,而是幫助你會用springboot的一些核心元件
從我的視角里,spring的容器生態就好比是一個戰無不勝攻無不克的鋼鐵俠,它在我們開發的過程中,提供這種武器支援,有IOC鐳射武器,有AOP生化武器
但是其實想要有一個這麼強大的"鋼鐵俠"作為我們平時開發的利器,離不開人工智慧AI,鋼鐵俠的靈魂"賈維斯"的存在,"賈維斯"就好比組裝和執行"鋼鐵俠"的管家,類比一下,賈維斯就是ApplicationContext.class,當“賈維斯”啟動按了開始按鈕的時候,就像spring的ApplicationContext呼叫了refresh方法,一鍵變身"鋼鐵俠"
訓練"賈維斯"的入口函式ApplicationContextInitializer載入ApplicationContextInitializer的幾種方式ApplicationContextInitializer的正確日常使用ApplicationContextInitializer的載入原理簡介ApplicationContextInitializer訓練"賈維斯"的入口叫做ApplicationContextInitializer,中文名叫做"應用上下文初始化器",優秀的spring原始碼,從名字就可以看到這個元件是幹嘛用的,簡而言之就是初始化ApplicationContext內部的一些屬性的,或者在ApplicationContext註冊一些什麼元件,這些都是可以的,下文我們也會寫例子,來具體說明ApplicationContextInitializer可以用來做的事情
再說說ApplicationContextInitializer在所有元件中的載入順序,我們先給結論,最後從原始碼的角度分析為啥它的載入順序是如此
它的順序是在EnvironmentPostProcessor之後,在refresh方法之前,如果你不知道refresh方法,也沒有關係,你只要知道它在所有的springboot元件中的載入順序是第二
再仔細思考一下為啥它的載入順序如此靠前,原因也很簡單,"籃子"EnvironementPostProcessor執行完了,把一些額外配置載入到籃子中了,接下來的一步就是建立一個"賈維斯"(鋼鐵俠的真正人工AI管家)——ApplicationContext,我覺得ApplicationContext也是spring的AI管家,在管家去操控spring容器例項這個"鋼鐵俠"之前,賈維斯需要把自己初始化好,訓練一下自己的AI智商,載入一些必要的元素,所以從這個角度來說,它的載入順序如此靠前也可以理解了
載入ApplicationContextInitializer的幾種方式方式一:利用META-INF下的spring.factories
1.1 在META-INF下宣告自定義的ApplicationContextInitializer與上篇文章載入EnvironmentPostProcessor一樣的方式
1.2 建立一個自定義的簡單的初始化器JarvisOneApplicationContextInitializer
1.3 啟動測試spring boot main函式,控制檯日誌輸出
方式二:在application.properties,定義一個key值為context.initializer.classes的鍵值對,value值指向自己的自定義的ApplicationContextInitializer
2.1 application.properties
2.2 自定義JarvisTwoApplicationContextInitializer
2.3 啟動測試spring boot main函式,控制檯日誌輸出
方法三:直接在啟動的main函式中指定自定義的ApplicationContextInitializer
3.1 在main函式中直接載入
3.2 自定義JarvisThreeApplicationContextInitializer
3.3 啟動測試spring boot main函式,控制檯日誌輸出
成功執行
方法四:在自定義的EnvironmentPostProcessor實現
4.2 啟動測試spring boot main函式,控制檯日誌輸出
如何正確的觸發賈維斯的訓練,大概的方式就是如上四種,推薦使用第一種,官方的,當然比較好,第二種比較冷門,而且容易被新手修改application.properties,第三種不是很靈活,第四種屬於投機取巧,我們要好好觸發賈維斯的訓練,否則智障的賈維斯最後組裝控制的鋼鐵俠肯定也是無法完成任務的
賈維斯的訓練器ApplicationContextInitializer正確使用方式其實說實話,這個初始化訓練"賈維斯"的元件ApplicationContextInitializer能做的事情特別多,為什麼這麼說呢,因為可以從它的定義的介面說起,如下圖,可以看出它就一個方法,initialize方法,這個方法的入參即合理又不合理,為什麼說合理,因為它初始化的就是這個ApplicationContext,所以用它作為入參,合情合理,為什麼說不合理,因為對於新手來說,ApplicationContext這個範圍太大了,不知道需要怎麼做才合理
為什麼說這裡的ApplicationContext太大了,很簡單,我們舉兩個例子來說就可以了
例一:如果在我們自定義的ApplicationContextInitializer中,使用configurableApplicationContext.getBean()方法就算不合理的,為什麼這麼說,請您想一想,這個時候,我們是要對"賈維斯"這個AI人工智慧進行訓練,載入一些特性,如果這個時候,"賈維斯"還沒有初始化好,更談不上ApplicationContext整個容器生態這個鋼鐵俠能不能戰鬥了,最後你用getBean這個"鋼鐵俠"發射導彈的功能,是不是有點過分了,會不會炸到自己?
例二:講一個種別人田的例子——稍微看過spring原始碼的人都知道(沒看過也沒有關係),ApplicationContext生態也就是這個鋼鐵俠是一個集大成者,它也是BeanDefinitionRegistry的一個具體實現,所以它可以去註冊一些BeanDefinition,如下圖虛擬碼所示,,這個程式碼有沒有問題,其實沒有大問題,但是它不合理,為啥這麼說,你想一下,註冊bean這種事情,就好比給鋼鐵俠去新增一些子彈,給他注入飛行等功能,這種事情應該是賈維斯去指派它的小弟去做的,但是在這個地方,我要你做的是初始化“賈維斯”
總而言之,用spring的元件就應該在正確的時候,用正確的元件做正確的事情
ApplicationContextInitializer正確的使用方式在我的個人片面理解裡,ApplicationContextInitializer能做的事情,算2件半
第一個0.5個事情
幫EnvironmentPostProcessor做一些擦屁股的事情,因為我們可以自定義很多EnvironmentPostProcessor的實現類,載入各式各樣的配置檔案,有本地的,有properties,有json格式的,有yaml格式的,甚至有遠端的,例如apollo,nacos等等,因為來源多樣,如果配置的properties的key值相同的時候,就會出現屬性配置衝突,這個時候就有一個優先順序的問題了,所以用ApplicationContextInitializer來做這個事,也是合適的,誰讓它這個元件是第二個被載入的呢?幫忙擦個屁股還是可以接受的,畢竟屁股總是要有人來擦的
但還是上面的說的原則,其實EnvironmentPostProcessor本身就可以做好這個事情,通過addFirst或者addLast,addFirst,addBefore,addAfter方法來確認配置屬性的優先順序,所以我覺得這個算半個事情
第一件完整事情
去釋出一些事件,spring有一個經典的模式,是事件的釋出和訂閱模式,後期文章也會介紹,在這邊,我們就說釋出和監聽的動作應該在ApplicationContextInitializer中去做,才是正確的時間做正確的事情,nacos就是大量利用釋出訂閱的模式來做屬性的熱更新的,我們這邊sample的虛擬碼如下
第二件完整的事情
第二件事情也是我們平時開發中,最經常使用的spring的技巧——在賈維斯的扳手裡面增加一個很使用的工具BeanFactoryPostProcessor,這個類在賈維斯的工具箱中,在組裝鋼鐵俠的過程中,有功不可沒的作用,後期文章再介紹,新增的方式如下
ApplicationContextInitializer的載入原理其實ApplicationContextInitializer載入順序的原理特別簡單,看過上一篇文章的小夥伴們,只要把原始碼往下多看一行就可以了,我們回到SpringApplication的#prepareContext方法中
這邊的原理還是非常簡單的,小夥伴們可以自行去掃一眼spring boot的原始碼,就能夠讀懂ApplicationContextInitializer了
小結本小節介紹了訓練賈維斯ApplicationContext的利器ApplicationContextInitializer,了解了它的載入時機和順序,以我個人的視角,說明了這個元件應該做和不應該做的事情,並且引出了下面spring boot核心元件系列的下幾個核心元件BeanFactoryPostProcessor和ApplicationEvent
如果您覺得不錯,希望你加一個關注,如果您覺得有任何錯誤的地方,歡迎後臺私信我,或者你有什麼想一起學的技術,可以告訴我,我們可以一起學習,您的關注是我寫作最大的動力~