面試官:好久沒見,甚是想念。今天來聊聊SpringBoot的自動設定吧?
候選者:嗯,SpringBoot的自動設定我覺得是SpringBoot很重要的「特性」了。眾所周知,SpringBoot有著「約定大於設定」的理念,這一理念一定程度上可以用「SpringBoot自動設定」來解釋。
候選者:SpringBoot自動設定的原理理解起來挺簡單的,我們在使用SpringBoot的時候,肯定會依賴於autoconfigure這麼一個包
候選者:autoconfigure這個包裡會有一個spring.factories檔案,該檔案定義了100+個入口的設定類。比如我們經常使用的redis、kafka等等這樣常見的中介軟體都預置了設定類
候選者:當我們在啟動SpringBoot專案的時候,內部就會載入這個spring.factories檔案,進而去載入「有需要」的設定類。那我們在使用相關元件的時候,就會非常的方便(因為設定類已經初始化了一大部分設定資訊)。
候選者:一般我們只要在application組態檔寫上對應的設定,就能通過各種template類直接操作對應的元件啦。
面試官:那是所有的設定類都會載入嗎?這個「有需要」的設定類你是怎麼理解的?
候選者:不是所有的設定類都會載入的,假設我們沒有引入redis-starter的包,那Redis的設定類就不會被載入。具體Spring在實現的時候就是使用 @ConditionalXXX進行判斷的。比如Redis的設定類就會有@ConditionalOnClass({RedisOperations.class})的設定,說明當前環境下如果有RedisOperations.class這個位元組碼,才會去載入Redis的設定類
面試官:哦,這樣啊,那瞭解了。那你知不知道Redis的設定類其實會有初始化RedisTemplate物件的操作,那假設我們沒有引入redis-starter包,那他是怎麼通過編譯的?(當然了,其他的設定類也是有可能有一樣的狀況)
候選者:嗯,這個我看原始碼的時候我也發現了。其實就是在autoconfigure包裡會定義到相關的依賴,但只是標記為optional並且只在編譯環境有效。那這樣是能通過編譯的,只是不會將其依賴傳入到我們的應用工程裡。
候選者:這塊還是花了我很多時間的,我最後在GitHub 的SpringBoot原始碼裡找到的。
面試官:嗯啊,有點東西的喲。既然都聊到這塊了,要不順便聊聊你對SpringBoot starter的理解?
候選者:嗯,starter這東西就是為了方便呼叫方去使用相關的元件的嘛,Spring框架也給我們實現了很多好用的starter。
候選者:比如以前我們要用Mybatis框架,可能會引入各種的包才能使用。而starter就是做了一層封裝,把相關要用到的jar都給包起來了,並且也寫好了對應的版本。這我們使用的時候就不需要引入一堆jar包且管理版本類似的問題了。
候選者:現在很多開源的元件都會提供對應的springboot-starter包給我們去用,要做一個starter包並不難。參照Spring內建的實現就好了:1、在工程裡引入 starter 打包相關的依賴。2、在我們工程內建spring.factories檔案,編寫我們設定類的全限類名。
面試官:嗯,大致都瞭解了,可以的。最後聊下你是怎麼看這塊原始碼的?
候選者:原始碼具體大概就不記得了,思路倒是還有的。我先從啟動類開始,會有個@SpringBootApplication,後面會定位到一個自動設定的註解@EnableAutoConfiguration,那最後就能看到註解內部會去META-INF/spring.factories載入設定類
候選者:這塊原始碼並不難,這個過程也瞭解到了原來maven有option和scope這倆標籤,但確實是SpringBoot比較重要的概念吧。
面試官:好嘞,今天到這就結束了吧。
題外:自動設定這個問題確實被問到過幾次。說實在的,對於Spring類、註解的資訊我真的記不住。感覺能答出這個流程思路,也就夠用了(如果面試官確實是要細究某個類名,那這種公司不去也罷)
約定大於設定:SpringBoot給我們內建了很多設定類,這些設定類也初始化了很多設定(預設值)。當我們要使用的時候,只需要覆蓋這些設定項就完事了。即便我們不寫,大多數情況下都不需要由我們顯示設定出來,但相關元件就能正常存取了。
如果想學Java專案的,我還是強烈推薦我的開源專案訊息推播平臺Austin,可以用作畢業設計,可以用作校招,可以看看生產環境是怎麼推播訊息的。
Gitee倉庫地址:https://gitee.com/zhongfucheng/austin
GitHub倉庫地址:https://github.com/ZhongFuCheng3y/austin