Spring Boot通常有一個名為*Application的入口類,在入口類里有一個main方法,這個main方法其實就是一個標準的java應用的入口方法。
在main方法中使用SpringApplication.run方法啟動SpringBoot應用項目。其中@SpringBootApplication是Spring Boot的核心注解,它是一個組合注解:
其中@SpringBootApplication注解主要組合了@Configuration、@EnableAutoConfiguration、@ComponentScan。如果不使用@SpringBootApplication注解,則可以使用在入口類上直接使用@Configuration、@EnableAutoConfiguration、@ComponentScan也能達到相同效果。其中幾個注解的作用大致說一下:@Configuration:是做類似于spring xml 工作的注解 標注在類上,類似與以前的**.xml配置文件。@EnableAutoConfiguration:spring boot自動配置時需要的注解,會讓Spring Boot根據類路徑中的jar包依賴為當前項目進行自動配置。同時,它也是一個組合注解。
在@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啟動過程:
在這里調用了SpringApplication的靜態run方法,并將Application類對象和main方法的參數args作為參數傳遞了進去。SpringApplication run方法:
在這個靜態方法中,創建并構造了SpringApplication對象,并調用該對象的run方法。構造SpringApplication對象:
主要是對一些屬性附上初始值,關鍵還在與SpringApplication對象的initialize方法。
SpringApplication類中的構造函數中調用initialize方法,初始化SpringApplication對象的成員變量sources,webEnvironment,initializers,listeners,mainApplicationClass。
sources: 我們傳給SpringApplication.run方法的參數。
webEnvironment:
可以看到webEnvironment是一個boolean,該成員變量用來表示當前應用程序是不是一個Web應用程序。通過在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES這個數組中所包含的類,如果存在那么當前程序即是一個Web應用程序,反之則不是。
initializers:
這里關鍵是調用SpringApplication對象中的getSpringFactoriesInstances方法,來獲取ApplicationContextInitializer類型對象的列表。
在該方法中,首先通過調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)來獲取所有Spring Factories的名字(在包spring-boot-版本.jar下)。
可以看到,是從一個名字叫spring.factories的資源文件中,讀取key為org.springframework.context.ApplicationContextInitializer的value。而spring.factories的部分內容如下:
我們從上面的spring.factories的資源文件中可以看到,得到的是 ConfigurationWarningsApplicationContextInitializer
ContextIdApplicationContextInitializer
DelegatingApplicationContextInitializer
ServerPortInfoApplicationContextInitializer這四個類的名字。然后調用createSpringFactoriesInstances方法根據讀取到的名字創建ApplicationContextInitializer實例(框起來的兩行代碼執行創建ApplicationContextInitializer實例)。
最后會將創建好的對象列表排序并返回。SpringApplication對象的成員變量initalizers就被初始化為,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer這四個類的對象組成的list(這是默認的成員變量initalizers初始化)。下面是幾個類的作用,這里不做詳細介紹,以后會有詳解介紹:
listeners:
listeners成員變量,是一個ApplicationListener<?>類型對象的集合。可以看到獲取該成員變量內容使用的是跟成員變量initializers一樣的方法,只不過傳入的類型從ApplicationContextInitializer.class變成了ApplicationListener.class。所以重點看spring.factories中取key為org.springframework.context.ApplicationListener的value有那些類:
也就是說,listener最終會被初始化為
ClearCachesApplicationListener
ParentContextCloserApplicationListener FileEncodingApplicationListener
AnsiOutputApplicationListener ConfigFileApplicationListener
DelegatingApplicationListener LiquibaseServiceLocatorApplicationListener
ClasspathLoggingApplicationListener LoggingApplicationListener
這幾個類的對象組成的list。何時觸發,這里不做詳解,等事件出現時,做詳細說明(這里有一張網上的圖,可以了解):
mainApplicationClass:
在deduceMainApplicationClass方法中,通過獲取當前調用棧,找到入口方法main所在的類,并將其復制給SpringApplication對象的成員變量mainApplicationClass。在我們的例子中mainApplicationClass即是我們自己編寫的Application類。
現在已經構造完SpringApplication對象了,之后就是調用該對象的run方法來運行spring boot項目。
run方法:?
StopWatch是來自org.springframework.util的工具類,可以用來方便的記錄程序的運行時間。
設置系統屬性java.awt.headless,在我們的例子中該屬性會被設置為true,因為我們開發的是服務器程序,一般運行在沒有顯示器和鍵盤的環境,但是還是需要相關一些數據,這樣我們就可以這樣設置系統屬性為headless模式。
run方法中,加載了一系列SpringApplicationRunListener對象,在創建和更新ApplicationContext方法前后分別調用了listeners對象的started方法和finished方法, 并在創建和刷新ApplicationContext時,將listeners作為參數傳遞到了createAndRefreshContext方法中,以便在創建和刷新ApplicationContext的不同階段,調用listeners的相應方法以執行操作。所以,所謂的SpringApplicationRunListeners實際上就是在SpringApplication對象的run方法執行的不同階段,去執行一些操作,并且這些操作是可配置的.創建ApplicationContext時調用startint方法
更新ApplicationContext方法時調用finished方法:
加載SpringApplicationRunListener,方式和initializers,listeners相同,所以我們可以在spring.factories中取key為org.springframework.boot.SpringApplicationRunListener的value可以看出加載了哪些類:
可以看出加載的是org.springframework.boot.context.event.EventPublishingRunListener類
EventPublishingRunListener:
EventPublishingRunListener對象在初始化時候將SpringApplication對象的成員變量listeners全都保存下來。等到自己的public方法被調用時,發布相應的事件,或執行相應的操作。RunListener是在SpringApplication對象的run方法執行到不同的階段時,發布相應的event給SpringApplication對象的成員變量listeners中記錄的事件監聽器。SpringApplicationRunListeners相關的類結構:
默認情況下,調用listeners的started方法,發布了ApplicationStartedEvent時,我們已經加載的事件監聽器都做了什么操作,(實現了ApplicationListener接口的類,并且事件類型是ApplicationStartingEvent類型)。
在默認情況下,classpath中不存在liquibase,所以不執行任何操作。LoggingApplicationListener監聽ApplicationStartedEvent,會根據classpath中的類情況創建相應的日志系統對象,并執行一些初始化之前的操作;
默認情況下,創建的是org.springframework.boot.logging.logback.LogbackLoggingSystem類的對象,Logback是SpringBoot默認采用的日志系統。
到這里,ApplicationStartedEvent事件的處理這樣就結束了。
創建并刷新ApplicationContext:
DefaultApplicationArguments:
這里是把main函數的args參數當做一個PropertySource來解析,默認情況下,args的長度是0,所以這里創建的DefaultApplicationArguments也沒有實際的內容。
ConfigurableEnvironment:創建并配置ApplicationConext的Environment
首先要調用getOrCreateEnvironment方法獲取一個Environment對象。
在默認情況下,執行到此處時,environment成員變量為null,而webEnvironment成員變量的值為true,所以會創建一個StandardServletEnvironment對象并返回。之后會調用ConfigurableEnvironment類型的對象的configureEnvironment方法來配置上一步獲取到的Environment對象。
configureEnvironment方法先是調用configurePropertySources來配置properties,然后調用configureProfiles來配置profiles。
這里,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:
configureProfiles首先會讀取Properties中key為spring.profiles.active的配置項,配置到Environment。
再將SpringApplication對象的成員變量additionalProfiles加入到Environment的active profiles配置中。默認情況下,配置文件里沒有spring.profiles.active的配置項,而SpringApplication對象的成員變量additionalProfiles也是一個空的集合,所以這個函數沒有配置任何active profile。
調用SpringApplicationRunListeners類的對象listeners發布ApplicationEnvironmentPreparedEvent事件:
到這一步時,Environment就算是配置完成了。接下來調用SpringApplicationRunListeners類的對象listeners發布ApplicationEnvironmentPreparedEvent事件。
等到發布完事件之后,我們就可以看看,加載的ApplicationListener對象都有哪些響應了這個事件,做了什么操作:
FileEncodingApplicationListener響應該事件:
檢查file.encoding配置是否與spring.mandatory_file_encoding一致
在默認情況下,因為沒有spring.mandatory_file_encoding的配置,所以這個響應方法什么都不做。AnsiOutputApplicationListener響應該事件:
根據spring.output.ansi.enabled和spring.output.ansi.console-available對AnsiOutput類做相應配置。在默認情況下,因為沒有做配置,所以這個響應方法什么都不做。ConfigFileApplicationListener響應該事件:
可以看到,ConfigFileApplicationListener從META-INF/spring.factories文件中讀取EnvironmentPostProcessor配置,加載相應的EnvironmentPostProcessor類的對象,并調用其postProcessEnvironment方法。在我們的例子中,會加載CloudFoundryVcapEnvironmentPostProcessor和SpringApplicationJsonEnvironmentPostProcessor并執行,由于我們的例子中沒有CloudFoundry和Json的配置,所以這個響應,不會加載任何的配置文件到Environment中來。
DelegatingApplicationListener響應該事件:將配置文件中key為context.listener.classes的配置項,加載在成員變量multicaster中
將配置文件中key為context.listener.classes的配置項,加載在成員變量multicaster中,因為在默認情況下沒有key為context.listener.classes的Property,所以不會加載任何listener到該監聽器中。
LoggingApplicationListener響應事件:對在ApplicationStarted時加載的LoggingSystem做一些初始化工作
默認情況下,是對加載的LogbackLoggingSystem做一些初始化工作。
打印Banner
printBanner方法中,首先會調用selectBanner方法得到一個banner對象,然后判斷bannerMode的類型,如果是Banner.Mode.LOG,那么將banner對象轉換為字符串,打印一條info日志,否則的話,調用banner對象的printbanner方法,將banner打印到標準輸出System.out。默認情況下,bannerMode是Banner.Mode.Console,而且也不曾提供過banner.txt這樣的資源文件。所以selectBanner方法中得到到便是默認的banner對象,即SpringBootBanner類的對象:
創建ApplicationContext:
SpringApplication中調用createApplicationContext獲取創建ApplicationContext(IOC容器),可以看到,當檢測到本次程序是一個web應用程序(成員變量webEnvironment為true)的時候,就加載類
DEFAULT_WEB_CONTEXT_CLASS,否則的話加載DEFAULT_CONTEXT_CLASS。我們的例子是一個web應用程序,所以會加載DEFAULT_WEB_CONTEXT_CLASS,也就是org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。
AnnotationConfigEmbeddedWebApplicationContext類的作用(功能):
可以看到我們加載的這個AnnotationConfigEmbeddedWebApplicationContext類,從名字就可以看出來,首先是一個WebApplicationContext實現了WebApplicationContext接口,然后是一個EmbeddedWebApplicationContext,這意味著它會自動創建并初始化一個EmbeddedServletContainer,同時還支持AnnotationConfig,會將使用注解標注的bean注冊到ApplicationContext中。總結起來就是:指定了容器的類名,最后通過Spring的工具類初始化容器類bean (BeanUtils.instantiate(contextClass))
通過調用Class對象的newInstance()方法來實例化對象,這等同于直接調用類的空的構造方法,所以我們來看AnnotationConfigEmbeddedWebApplicationContext類的構造方法:
構造方法中初始化了兩個成員變量,類型分別為AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner用以加載使用注解的bean定義。
這樣ApplicationContext對象就創建出來了,在createAndRefreshContext方法中創建了ApplicationContext對象之后會緊接著調用其setEnvironment將我們之前準備好的Environment對象賦值進去。之后分別調用postProcessApplicationContext和applyInitializers做一些處理和初始化的操作。
如果成員變量beanNameGenerator不為Null,那么為ApplicationContext對象注冊beanNameGenerator bean。如果成員變量resourceLoader不為null,則為ApplicationContext對象設置ResourceLoader。默認情況下,這兩個成員變量都為Null,所以什么都不做。
最后刷新上下文:
這里調用的是AbstractApplicationContext的refresh()方法刷新上下文。
1、this.prepareRefresh();
準備啟動spring容器,設置容器的啟動日期和活動標志2、ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
主要是創建beanFactory,同時加載配置文件.xml中的beanDefinition
通過String[] configLocations = getConfigLocations()獲取資源路徑,然后加載beanDefinition 3、this.prepareBeanFactory(beanFactory);
給beanFactory注冊一些標準組件,如ClassLoader,StandardEnvironment,BeanProcess 4、this.postProcessBeanFactory(beanFactory);
提供給子類實現一些postProcess的注冊,如AbstractRefreshableWebApplicationContext注冊一些Servlet相關的postProcess,真對web進行生命周期管理的Scope,通過registerResolvableDependency()方法注冊指定ServletRequest,HttpSession,WebRequest對象的工廠方法。5、this.invokeBeanFactoryPostProcessors(beanFactory);
調用所有BeanFactoryProcessor的postProcessBeanFactory()方法 6、this.registerBeanPostProcessors(beanFactory);
注冊BeanPostProcessor,BeanPostProcessor作用是用于攔截Bean的創建 7、this.initMessageSource();
初始化消息Bean 8、this.initApplicationEventMulticaster();
初始化上下文的事件多播組建,ApplicationEvent觸發時由multicaster通知給ApplicationListener 9、this.onRefresh();
ApplicationContext初始化一些特殊的bean 10、this.registerListeners();
注冊事件監聽器,事件監聽Bean統一注冊到multicaster里頭,ApplicationEvent事件觸發后會由multicaster廣播 11、this.finishBeanFactoryInitialization(beanFactory);
非延遲加載的單例Bean實例化12、this.finishRefresh();
結束刷新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
結束刷新容器之后執行的afterRefresh()方法:
實際應用中,我們會有在項目服務啟動的時候就去加載一些數據或做一些事情這樣的需求。比如讀取配置文件,數據庫連接之類的。SpringBoot給我們提供了兩個接口來幫助我們實現這種需求。這兩個接口分別為CommandLineRunner和ApplicationRunner。他們的執行時機為容器啟動完成的時候。
這兩個接口中有一個run方法,我們只需要實現這個方法即可。這兩個接口的不同之處在于:ApplicationRunner中run方法的參數為ApplicationArguments,而CommandLineRunner接口中run方法的參數為String數組。
結束監聽器:
從上述代碼中可以看出,finished()方法是通過發布**結束監聽**這個事件,當實現onApplication(相應事件)的監聽類監聽到這個事件時,就會結束相應的監聽。
結束計時:
stopWatch.stop();
開始打印Log信息:
找到你的入口函數(xxApplication.class),開始打印Log信息。
總結
以上是生活随笔 為你收集整理的SpringBoot启动过程详解 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。