SpringBoot啟動
Spring Boot通常有一個名為*Application的入口類,在入口類裡有一個main方法,這個main方法其實就是一個標準的java應用的入口方法。在main方法中使用SpringApplication.run方法啟動SpringBoot應用項目。其中@SpringBootApplication是Spring Boot的核心註解,它是一個組合註解:
1 2 3 4 5 6
其中@SpringBootApplication註解主要組合了@Configuration、@EnableAutoConfiguration、@ComponentScan。如果不使用@SpringBootApplication註解,則可以使用在入口類上直接使用@Configuration、@EnableAutoConfiguration、@ComponentScan也能達到相同效果。其中幾個註解的作用大致說一下:@Configuration:是做類似於spring xml 工作的註解 標註在類上,類似與以前的**.xml設定檔。@EnableAutoConfiguration:spring boot自動設定時需要的註解,會讓Spring Boot根據類路徑中的jar包依賴為當前項目進行自動設定。同時,它也是一個組合註解。
1 2 3 4 5 6 7 8 9 10 11
在@EnableAutoConfiguration中用了@Import註解匯入EnableAutoConfigurationImportSelector類,而EnableAutoConfigurationImportSelector就是自動設定關鍵。@Import:Spring4.2之前只支援匯入配置類 Spring4.2之後支援匯入普通的java類,並將其聲明成一個bean@ComponentScan:告訴Spring 哪個packages 的用註解標識的類 會被spring自動掃描並且裝入bean容器。SpringBoot的自動設定:SpringBoot的一大特色就是自動設定,例如:添加了spring-boot-starter-web依賴,會自動添加Tomcat和SpringMVC的依賴,SpringBoot會對Tomcat和SpringMVC進行自動設定。又例如:添加了spring-boot-starter-data-jpa依賴,SpringBoot會自動進行JPA相關的配置。SpringBoot會自動掃描@SpringBootApplication所在類的同級包以及下級包的Bean(如果為JPA項目還可以掃描標註@Entity的實體類),所以建議入口類放置在最外層包下。spring-boot啟動過程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
在這裡調用了SpringApplication的靜態run方法,並將Application類對象和main方法的參數args作為參數傳遞了進去。SpringApplication run方法:
1 2 3 4 5
在這個靜態方法中,建立並構造了SpringApplication對象,並調用該對象的run方法。構造SpringApplication對象:
1 2 3 4
主要是對一些屬性附上初始值,關鍵還在與SpringApplication對象的initialize方法。
1 2
SpringApplication類中的建構函式中調用initialize方法,初始化SpringApplication對象的成員變數sources,webEnvironment,initializers,listeners,mainApplicationClass。
1 2
sources: 我們傳給SpringApplication.run方法的參數。
webEnvironment:
可以看到webEnvironment是一個boolean,該成員變數用來表示當前應用程式是不是一個Web應用程式。通過在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES這個數組中所包含的類,如果存在那麼當前程式即是一個Web應用程式,反之則不是。
1 2
initializers:
這裡關鍵是調用SpringApplication對象中的getSpringFactoriesInstances方法,來擷取ApplicationContextInitializer類型對象的列表。
1 2
在該方法中,首先通過調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)來擷取所有Spring Factories的名字(在包spring-boot-版本.jar下)。
1 2
可以看到,是從一個名字叫spring.factories的資源檔中,讀取key為org.springframework.context.ApplicationContextInitializer的value。而spring.factories的部分內容如下:
1 2
我們從上面的spring.factories的資源檔中可以看到,得到的是 ConfigurationWarningsApplicationContextInitializerContextIdApplicationContextInitializerDelegatingApplicationContextInitializerServerPortInfoApplicationContextInitializer這四個類的名字。然後調用createSpringFactoriesInstances方法根據讀取到的名字建立ApplicationContextInitializer執行個體(框起來的兩行代碼執行建立ApplicationContextInitializer執行個體)。
1 2 3 4 5 6 7
最後會將建立好的對象列表排序並返回。SpringApplication對象的成員變數initalizers就被初始化為,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer這四個類的對象組成的list(這是預設的成員變數initalizers初始化)。下面是幾個類的作用,這裡不做詳細介紹,以後會有詳解介紹:
1 2 3 4 5 6
listeners:
listeners成員變數,是一個ApplicationListener<?>類型對象的集合。可以看到擷取該成員變數內容使用的是跟成員變數initializers一樣的方法,只不過傳入的類型從ApplicationContextInitializer.class變成了ApplicationListener.class。所以重點看spring.factories中取key為org.springframework.context.ApplicationListener的value有那些類:
1 2 3 4 5
也就是說,listener最終會被初始化為ClearCachesApplicationListenerParentContextCloserApplicationListener FileEncodingApplicationListenerAnsiOutputApplicationListener ConfigFileApplicationListenerDelegatingApplicationListener LiquibaseServiceLocatorApplicationListenerClasspathLoggingApplicationListener LoggingApplicationListener這幾個類的對象組成的list。何時觸發,這裡不做詳解,等事件出現時,做詳細說明(這裡有一張網上的圖,可以瞭解):
1 2 3 4 5 6 7 8 9 10
mainApplicationClass:
在deduceMainApplicationClass方法中,通過擷取當前調用棧,找到入口方法main所在的類,並將其複製給SpringApplication對象的成員變數mainApplicationClass。在我們的例子中mainApplicationClass即是我們自己編寫的Application類。
1 2
現在已經構造完SpringApplication對象了,之後就是調用該對象的run方法來運行spring boot項目。
run方法:
StopWatch是來自org.springframework.util的工具類,可以用來方便的記錄程式的已耗用時間。
1 2
設定系統屬性java.awt.headless,在我們的例子中該屬性會被設定為true,因為我們開發的是伺服器程式,一般運行在沒有顯示器和鍵盤的環境,但是還是需要相關一些資料,這樣我們就可以這樣設定系統屬性為headless模式。
1 2
run方法中,載入了一系列SpringApplicationRunListener對象,在建立和更新ApplicationContext方法前後分別調用了listeners對象的started方法和finished方法, 並在建立和重新整理ApplicationContext時,將listeners作為參數傳遞到了createAndRefreshContext方法中,以便在建立和重新整理ApplicationContext的不同階段,調用listeners的相應方法以執行操作。所以,所謂的SpringApplicationRunListeners實際上就是在SpringApplication對象的run方法執行的不同階段,去執行一些操作,並且這些操作是可配置的.建立ApplicationContext時調用startint方法
1 2 3 4
更新ApplicationContext方法時調用finished方法:
1 2
載入SpringApplicationRunListener,方式和initializers,listeners相同,所以我們可以在spring.factories中取key為org.springframework.boot.SpringApplicationRunListener的value可以看出載入了哪些類:
1 2
可以看出載入的是org.springframework.boot.context.event.EventPublishingRunListener類
1 2
EventPublishingRunListener:
1 2
EventPublishingRunListener對象在初始化時候將SpringApplication對象的成員變數listeners全都儲存下來。等到自己的public方法被調用時,發布相應的事件,或執行相應的操作。RunListener是在SpringApplication對象的run方法執行到不同的階段時,發布相應的event給SpringApplication對象的成員變數listeners中記錄的事件監聽器。SpringApplicationRunListeners相關的類結構:
1 2 3 4 5 6 7 8 9
預設情況下,調用listeners的started方法,發布了ApplicationStartedEvent時,我們已經載入的事件監聽器都做了什麼操作,(實現了ApplicationListener介面的類,並且事件類型是ApplicationStartingEvent類型)。
1 2
在預設情況下,classpath中不存在liquibase,所以不執行任何操作。LoggingApplicationListener監聽ApplicationStartedEvent,會根據classpath中的類情況建立相應的日誌系統對象,並執行一些初始化之前的操作;
1 2 3 4
預設情況下,建立的是org.springframework.boot.logging.logback.LogbackLoggingSystem類的對象,Logback是SpringBoot預設採用的日誌系統。
1 2
到這裡,ApplicationStartedEvent事件的處理這樣就結束了。
1 2
建立並重新整理ApplicationContext:
DefaultApplicationArguments:
1 2
這裡是把main函數的args參數當做一個PropertySource來解析,預設情況下,args的長度是0,所以這裡建立的DefaultApplicationArguments也沒有實際的內容。
1 2
ConfigurableEnvironment:建立並配置ApplicationConext的Environment
首先要調用getOrCreateEnvironment方法擷取一個Environment對象。
1 2
在預設情況下,執行到此處時,environment成員變數為null,而webEnvironment成員變數的值為true,所以會建立一個StandardServletEnvironment對象並返回。之後會調用ConfigurableEnvironment類型的對象的configureEnvironment方法來配置上一步擷取到的Environment對象。
1 2 3 4
configureEnvironment方法先是調用configurePropertySources來配置properties,然後調用configureProfiles來配置profiles。
1 2
這裡,configurePropertySources首先查看SpringApplication對象的成員變數defaultProperties,如果該變數非null且內容非空,則將其加入到Environment的PropertySource列表的最後。之後查看SpringApplication對象的成員變數addCommandLineProperties和main函數的參數args,如果設定了addCommandLineProperties=true,且args個數大於0,那麼就構造一個由main函數的參數組成的PropertySource放到Environment的PropertySource列表的最前面(這就能保證,我們通過main函數的參數來做的配置是最優先的,可以覆蓋其他配置)。在預設情況下,由於沒有配置defaultProperties且main函數的參數args個數為0,所以這個函數什麼也不做。調用configureProfiles來配置profiles:
1 2 3 4 5 6 7 8
configureProfiles首先會讀取Properties中key為spring.profiles.active的配置項,配置到Environment。
1 2
再將SpringApplication對象的成員變數additionalProfiles加入到Environment的active profiles配置中。預設情況下,設定檔裡沒有spring.profiles.active的配置項,而SpringApplication對象的成員變數additionalProfiles也是一個空的集合,所以這個函數沒有配置任何active profile。
1 2 3 4
調用SpringApplicationRunListeners類的對象listeners發布ApplicationEnvironmentPreparedEvent事件:
到這一步時,Environment就算是配置完成了。接下來調用SpringApplicationRunListeners類的對象listeners發布ApplicationEnvironmentPreparedEvent事件。
1 2
等到發布完事件之後,我們就可以看看,載入的ApplicationListener對象都有哪些響應了這個事件,做了什麼操作:
FileEncodingApplicationListener響應該事件:
1 2
檢查file.encoding配置是否與spring.mandatory_file_encoding一致在預設情況下,因為沒有spring.mandatory_file_encoding的配置,所以這個回應程式法什麼都不做。AnsiOutputApplicationListener響應該事件:
1 2 3 4 5 6
根據spring.output.ansi.enabled和spring.output.ansi.console-available對AnsiOutput類做相應配置。在預設情況下,因為沒有做配置,所以這個回應程式法什麼都不做。ConfigFileApplicationListener響應該事件:
1 2 3 4 5
可以看到,ConfigFileApplicationListener從META-INF/spring.factories檔案中讀取EnvironmentPostProcessor配置,載入相應的EnvironmentPostProcessor類的對象,並調用其postProcessEnvironment方法。在我們的例子中,會載入CloudFoundryVcapEnvironmentPostProcessor和SpringApplicationJsonEnvironmentPostProcessor並執行,由於我們的例子中沒有CloudFoundry和Json的配置,所以這個響應,不會載入任何的設定檔到Environment中來。
1 2
DelegatingApplicationListener響應該事件:將設定檔中key為context.listener.classes的配置項,載入在成員變數multicaster中