? ? ? 相對于spring mvc,spring boot并不是一個新的框架,給人最直觀的感受就是不需要spring mvc那樣寫一堆配置,因為做了大量的自動化配置,當然這只是最直觀的感受。所以我們來看看boot(以下spring boot簡稱boot)的啟動過程,試圖去了解內部到底做了什么。文中若有錯誤之處,請指正。
? ? ? 這里框架demo的項目搭建這里就不展示了,比較簡單。我們先從入口類開始,例子中的入口類展示如下,
此類只有一個main方法,可以很清晰的看到有2個關鍵的信息點:1.@SpringBootApplication注解,2.SpringApplication.run靜態方法。所以點開SpringApplication的源碼吧。源碼中第一步跟到static run方法。
可以看到里面做了兩件事情,一是構造函數,二是run方法。我們先看構造函數做了什么。
一、構造函數
兩個重載的構造函數,例子中調用的是第一個,前幾行做了一些成員變量的初始化,我們先略去每個成員變量的具體意義,直接看這個方法this.initialize(sources);列子中參數sources為空,不用管。然后是this.webEnvironment=this.deduceWebEnvironment(); webEnvironment代表是否是web環境。
1.我們看deduceWebEnvironment方法是怎么做的。
? ? ? ? ? ? ? ? ? ? ??
其中WEB_ENVIRONMENT_CLASSES =newString[]{"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"}; 該方法的關鍵的地方ClassUtils.isPresent主要就是根據WEB_ENVIRONMENT_CLASSES中的2個類名來查找classpath下面是否有2個class文件,如果都有webEnvironment則為true,否則為false。
2.接著我們看構造函數中的下一行,this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));主要看getSpringFactoriesInstances。我們截取源碼中的改方法的代碼片段
我們可以知道片段1的getSpringFactoriesInstances()中的參數type為ApplicationContextInitializer.class對象。緊接著就調用它底下的一個重載方法。代碼2中是獲取當前線程的一個類加載器。然后是3中SpringFactories.loadFactoryNames,此方法會查找classpath下面的META-INF/spring.factories文件中的類名,我們點開文件看下。
請注意classpath下面不止這一個spring.factorys文件,在這不全部展示,總之改方法會加載ApplicationContextInitializer對應的一系列類名,如上圖中藍色部分。這些類都是ApplicationContextInitializer的實現類。然后圖片(片段1)中的方法4 就是把3中獲取到的類名利用反射構造這些對象的實例。至此,setInitializers 方法中已經獲取到以下這類的實例對象的集合。
3.輪到setListeners,跟setInitializers方法過程一樣,只不過參數為ApplicationListener.class。最終setListeners幫助SpringApplication獲取到了一系列ApplicationListener的實現類的對象的集合,具體哪些對象,看下圖。
好了,我們在回到構造函數,走到這里了,this.mainApplicationClass=this.deduceMainApplicationClass();此步驟比較簡單,就是獲取我們的入口類CamelliaApplication的class對象。
至此,構造函數已經全部走完,主要做的事情就是1,初始化一些變量,2,實例化ApplicationContextInitializer的一系列的子類 3,實例化ApplicationListener一系列的子類。3,獲取入口的class對象。
留下的疑問點:1,構造函數中初始化的成員變量的意義,2.得到ApplicationContextInitializer和ApplicationListener的一些列對象是干什么的。我們還有很長的路走,別急。