SpringBoot的啟動很簡單,代碼如下:
@SpringBootApplicationpublicclassMyApplication{publicstaticvoidmain(String[] args){? ? ? ? ? ? SpringApplication.run(MyApplication.class, args);? ? ? ? }? ? }
從代碼上可以看出,調(diào)用了SpringApplication的靜態(tài)方法run。這個run方法會構(gòu)造一個SpringApplication的實例,然后再調(diào)用這里實例的run方法就表示啟動SpringBoot。
因此,想要分析SpringBoot的啟動過程,我們需要熟悉SpringApplication的構(gòu)造過程以及SpringApplication的run方法執(zhí)行過程即可。
我們以上述這段代碼為例,分析SpringBoot的啟動過程。
SpringApplication的構(gòu)造過程
SpringApplication構(gòu)造的時候內(nèi)部會調(diào)用一個private方法initialize:
publicSpringApplication(Object... sources){? ? ? initialize(sources);// sources目前是一個MyApplication的class對象}privatevoidinitialize(Object[] sources){if(sources !=null&& sources.length >0) {this.sources.addAll(Arrays.asList(sources));// 把sources設(shè)置到SpringApplication的sources屬性中,目前只是一個MyApplication類對象}this.webEnvironment = deduceWebEnvironment();// 判斷是否是web程序(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都必須在類加載器中存在),并設(shè)置到webEnvironment屬性中// 從spring.factories文件中找出key為ApplicationContextInitializer的類并實例化后設(shè)置到SpringApplication的initializers屬性中。這個過程也就是找出所有的應(yīng)用程序初始化器setInitializers((Collection) getSpringFactoriesInstances(? ? ? ? ? ApplicationContextInitializer.class));// 從spring.factories文件中找出key為ApplicationListener的類并實例化后設(shè)置到SpringApplication的listeners屬性中。這個過程就是找出所有的應(yīng)用程序事件監(jiān)聽器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 找出main類,這里是MyApplication類this.mainApplicationClass = deduceMainApplicationClass();? ? }
ApplicationContextInitializer,應(yīng)用程序初始化器,做一些初始化的工作:
publicinterfaceApplicationContextInitializer{voidinitialize(C applicationContext);? ? }
ApplicationListener,應(yīng)用程序事件(ApplicationEvent)監(jiān)聽器:
publicinterfaceApplicationListenerextendsEventListener{voidonApplicationEvent(E event);? ? }
這里的應(yīng)用程序事件(ApplicationEvent)有應(yīng)用程序啟動事件(ApplicationStartedEvent),失敗事件(ApplicationFailedEvent),準(zhǔn)備事件(ApplicationPreparedEvent)等。
應(yīng)用程序事件監(jiān)聽器跟監(jiān)聽事件是綁定的。比如ConfigServerBootstrapApplicationListener只跟ApplicationEnvironmentPreparedEvent事件綁定,LiquibaseServiceLocatorApplicationListener只跟ApplicationStartedEvent事件綁定,LoggingApplicationListener跟所有事件綁定等。
默認(rèn)情況下,initialize方法從spring.factories文件中找出的key為ApplicationContextInitializer的類有:
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
key為ApplicationListener的有:
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
SpringApplication的執(zhí)行
分析run方法之前,先看一下SpringApplication中的一些事件和監(jiān)聽器概念。
首先是SpringApplicationRunListeners類和SpringApplicationRunListener類的介紹。
SpringApplicationRunListeners內(nèi)部持有SpringApplicationRunListener集合和1個Log日志類。用于SpringApplicationRunListener監(jiān)聽器的批量執(zhí)行。
SpringApplicationRunListener看名字也知道用于監(jiān)聽SpringApplication的run方法的執(zhí)行。
它定義了5個步驟:
started(run方法執(zhí)行的時候立馬執(zhí)行;對應(yīng)事件的類型是ApplicationStartedEvent)
environmentPrepared(ApplicationContext創(chuàng)建之前并且環(huán)境信息準(zhǔn)備好的時候調(diào)用;對應(yīng)事件的類型是ApplicationEnvironmentPreparedEvent)
contextPrepared(ApplicationContext創(chuàng)建好并且在source加載之前調(diào)用一次;沒有具體的對應(yīng)事件)
contextLoaded(ApplicationContext創(chuàng)建并加載之后并在refresh之前調(diào)用;對應(yīng)事件的類型是ApplicationPreparedEvent)
finished(run方法結(jié)束之前調(diào)用;對應(yīng)事件的類型是ApplicationReadyEvent或ApplicationFailedEvent)
SpringApplicationRunListener目前只有一個實現(xiàn)類EventPublishingRunListener,它把監(jiān)聽的過程封裝成了SpringApplicationEvent事件并讓內(nèi)部屬性(屬性名為multicaster)ApplicationEventMulticaster接口的實現(xiàn)類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件對象會被SpringApplication中的listeners屬性進行處理。
所以說SpringApplicationRunListener和ApplicationListener之間的關(guān)系是通過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯(lián)系起來的。
SpringApplication的run方法代碼如下:
publicConfigurableApplicationContextrun(String... args){? ? ? StopWatch stopWatch =newStopWatch();// 構(gòu)造一個任務(wù)執(zhí)行觀察器stopWatch.start();// 開始執(zhí)行,記錄開始時間ConfigurableApplicationContext context =null;? ? ? configureHeadlessProperty();// 獲取SpringApplicationRunListeners,內(nèi)部只有一個EventPublishingRunListenerSpringApplicationRunListeners listeners = getRunListeners(args);// 上面分析過,會封裝成SpringApplicationEvent事件然后廣播出去給SpringApplication中的listeners所監(jiān)聽// 這里接受ApplicationStartedEvent事件的listener會執(zhí)行相應(yīng)的操作listeners.started();try{// 構(gòu)造一個應(yīng)用程序參數(shù)持有類ApplicationArguments applicationArguments =newDefaultApplicationArguments(? ? ? ? ? ? args);// 創(chuàng)建Spring容器context = createAndRefreshContext(listeners, applicationArguments);// 容器創(chuàng)建完成之后執(zhí)行額外一些操作afterRefresh(context, applicationArguments);// 廣播出ApplicationReadyEvent事件給相應(yīng)的監(jiān)聽器執(zhí)行l(wèi)isteners.finished(context,null);? ? ? ? stopWatch.stop();// 執(zhí)行結(jié)束,記錄執(zhí)行時間if(this.logStartupInfo) {newStartupInfoLogger(this.mainApplicationClass)? ? ? ? ? ? ? .logStarted(getApplicationLog(), stopWatch);? ? ? ? }returncontext;// 返回Spring容器}catch(Throwable ex) {? ? ? ? handleRunFailure(context, listeners, ex);// 這個過程報錯的話會執(zhí)行一些異常操作、然后廣播出ApplicationFailedEvent事件給相應(yīng)的監(jiān)聽器執(zhí)行thrownewIllegalStateException(ex);? ? ? }? ? }
創(chuàng)建容器的方法createAndRefreshContext如下:
privateConfigurableApplicationContextcreateAndRefreshContext(
? ? ? ? SpringApplicationRunListeners listeners,
? ? ? ? ApplicationArguments applicationArguments){? ? ? ConfigurableApplicationContext context;// 定義Spring容器// 創(chuàng)建應(yīng)用程序的環(huán)境信息。如果是web程序,創(chuàng)建StandardServletEnvironment;否則,創(chuàng)建StandardEnvironmentConfigurableEnvironment environment = getOrCreateEnvironment();// 配置一些環(huán)境信息。比如profile,命令行參數(shù)configureEnvironment(environment, applicationArguments.getSourceArgs());// 廣播出ApplicationEnvironmentPreparedEvent事件給相應(yīng)的監(jiān)聽器執(zhí)行l(wèi)isteners.environmentPrepared(environment);// 環(huán)境信息的校對if(isWebEnvironment(environment) && !this.webEnvironment) {? ? ? ? environment = convertToStandardEnvironment(environment);? ? ? }if(this.bannerMode != Banner.Mode.OFF) {// 是否在控制臺上打印自定義的bannerprintBanner(environment);? ? ? }// Create, load, refresh and run the ApplicationContextcontext = createApplicationContext();// 創(chuàng)建Spring容器context.setEnvironment(environment);// 設(shè)置Spring容器的環(huán)境信息postProcessApplicationContext(context);// 回調(diào)方法,Spring容器創(chuàng)建之后做一些額外的事applyInitializers(context);// SpringApplication的的初始化器開始工作// 遍歷調(diào)用SpringApplicationRunListener的contextPrepared方法。目前只是將這個事件廣播器注冊到Spring容器中l(wèi)isteners.contextPrepared(context);if(this.logStartupInfo) {? ? ? ? logStartupInfo(context.getParent() ==null);? ? ? ? logStartupProfileInfo(context);? ? ? }// 把應(yīng)用程序參數(shù)持有類注冊到Spring容器中,并且是一個單例context.getBeanFactory().registerSingleton("springApplicationArguments",? ? ? ? ? applicationArguments);? ? ? Set sources = getSources();? ? ? Assert.notEmpty(sources,"Sources must not be empty");? ? ? load(context, sources.toArray(newObject[sources.size()]));// 廣播出ApplicationPreparedEvent事件給相應(yīng)的監(jiān)聽器執(zhí)行l(wèi)isteners.contextLoaded(context);// Spring容器的刷新refresh(context);if(this.registerShutdownHook) {try{? ? ? ? ? context.registerShutdownHook();? ? ? ? }catch(AccessControlException ex) {// Not allowed in some environments.}? ? ? }returncontext;? ? }
Spring容器的創(chuàng)建createApplicationContext方法如下:
protectedConfigurableApplicationContextcreateApplicationContext(){? ? ? Class contextClass =this.applicationContextClass;if(contextClass ==null) {try{// 如果是web程序,那么構(gòu)造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器// 否則構(gòu)造org.springframework.context.annotation.AnnotationConfigApplicationContext容器contextClass = Class.forName(this.webEnvironment? ? ? ? ? ? ? ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);? ? ? ? }catch(ClassNotFoundException ex) {thrownewIllegalStateException("Unable create a default ApplicationContext, "+"please specify an ApplicationContextClass",? ? ? ? ? ? ? ex);? ? ? ? }? ? ? }return(ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);? ? }
Spring容器創(chuàng)建之后有個回調(diào)方法postProcessApplicationContext:
protectedvoidpostProcessApplicationContext(ConfigurableApplicationContext context){if(this.webEnvironment) {// 如果是web程序if(contextinstanceofConfigurableWebApplicationContext) {// 并且也是Spring Web容器ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context;if(this.beanNameGenerator !=null) {// 如果SpringApplication設(shè)置了是實例命名生成器,注冊到Spring容器中configurableContext.getBeanFactory().registerSingleton(? ? ? ? ? ? ? ? ? ? ? ? ? ? AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }if(this.resourceLoader !=null) {// 如果SpringApplication設(shè)置了資源加載器,設(shè)置到Spring容器中if(contextinstanceofGenericApplicationContext) {? ? ? ? ? ? ? ? ((GenericApplicationContext) context)? ? ? ? ? ? ? ? ? ? ? ? .setResourceLoader(this.resourceLoader);? ? ? ? ? ? }if(contextinstanceofDefaultResourceLoader) {? ? ? ? ? ? ? ? ((DefaultResourceLoader) context)? ? ? ? ? ? ? ? ? ? ? ? .setClassLoader(this.resourceLoader.getClassLoader());? ? ? ? ? ? }? ? ? ? }? ? }
初始化器做的工作,比如ContextIdApplicationContextInitializer會設(shè)置應(yīng)用程序的id;AutoConfigurationReportLoggingInitializer會給應(yīng)用程序添加一個條件注解解析器報告等:
protectedvoidapplyInitializers(ConfigurableApplicationContext context){// 遍歷每個初始化器,對調(diào)用對應(yīng)的initialize方法for(ApplicationContextInitializer initializer : getInitializers()) {? ? ? ? Class requiredType = GenericTypeResolver.resolveTypeArgument(? ? ? ? ? ? initializer.getClass(), ApplicationContextInitializer.class);? ? ? ? Assert.isInstanceOf(requiredType, context,"Unable to call initializer.");? ? ? ? initializer.initialize(context);? ? ? }? ? }
Spring容器的刷新refresh方法內(nèi)部會做很多很多的事情:比如BeanFactory的設(shè)置,BeanFactoryPostProcessor接口的執(zhí)行、BeanPostProcessor接口的執(zhí)行、自動化配置類的解析、條件注解的解析、國際化的初始化等等。這部分內(nèi)容會在之后的文章中進行講解。
run方法中的Spring容器創(chuàng)建完成之后會調(diào)用afterRefresh方法,代碼如下:
protectedvoidafterRefresh(ConfigurableApplicationContext context,
? ? ? ? ApplicationArguments args){? ? ? afterRefresh(context, args.getSourceArgs());// 目前是個空實現(xiàn)callRunners(context, args);// 調(diào)用Spring容器中的ApplicationRunner和CommandLineRunner接口的實現(xiàn)類}privatevoidcallRunners(ApplicationContext context, ApplicationArguments args){? ? ? ? List runners =newArrayList();// 找出Spring容器中ApplicationRunner接口的實現(xiàn)類runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());// 找出Spring容器中CommandLineRunner接口的實現(xiàn)類runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());// 對runners進行排序AnnotationAwareOrderComparator.sort(runners);// 遍歷runners依次執(zhí)行for(Object runner :newLinkedHashSet(runners)) {if(runnerinstanceofApplicationRunner) {// 如果是ApplicationRunner,進行ApplicationRunner的run方法調(diào)用callRunner((ApplicationRunner) runner, args);? ? ? ? ? ? }if(runnerinstanceofCommandLineRunner) {// 如果是CommandLineRunner,進行CommandLineRunner的run方法調(diào)用callRunner((CommandLineRunner) runner, args);? ? ? ? ? ? }? ? ? ? }? ? }
這樣run方法執(zhí)行完成之后。Spring容器也已經(jīng)初始化完成,各種監(jiān)聽器和初始化器也做了相應(yīng)的工作。
總結(jié)
SpringBoot啟動的時候,不論調(diào)用什么方法,都會構(gòu)造一個SpringApplication的實例,然后調(diào)用這個實例的run方法,這樣就表示啟動SpringBoot。
在run方法調(diào)用之前,也就是構(gòu)造SpringApplication的時候會進行初始化的工作,初始化的時候會做以下幾件事:
把參數(shù)sources設(shè)置到SpringApplication屬性中,這個sources可以是任何類型的參數(shù)。本文的例子中這個sources就是MyApplication的class對象
判斷是否是web程序,并設(shè)置到webEnvironment這個boolean屬性中
找出所有的初始化器,默認(rèn)有5個,設(shè)置到initializers屬性中
找出所有的應(yīng)用程序監(jiān)聽器,默認(rèn)有9個,設(shè)置到listeners屬性中
找出運行的主類(main class)
SpringApplication構(gòu)造完成之后調(diào)用run方法,啟動SpringApplication,run方法執(zhí)行的時候會做以下幾件事:
構(gòu)造一個StopWatch,觀察SpringApplication的執(zhí)行
找出所有的SpringApplicationRunListener并封裝到SpringApplicationRunListeners中,用于監(jiān)聽run方法的執(zhí)行。監(jiān)聽的過程中會封裝成事件并廣播出去讓初始化過程中產(chǎn)生的應(yīng)用程序監(jiān)聽器進行監(jiān)聽
構(gòu)造Spring容器(ApplicationContext),并返回
3.1 創(chuàng)建Spring容器的判斷是否是web環(huán)境,是的話構(gòu)造AnnotationConfigEmbeddedWebApplicationContext,否則構(gòu)造AnnotationConfigApplicationContext
3.2 初始化過程中產(chǎn)生的初始化器在這個時候開始工作
3.3 Spring容器的刷新(完成bean的解析、各種processor接口的執(zhí)行、條件注解的解析等等)
從Spring容器中找出ApplicationRunner和CommandLineRunner接口的實現(xiàn)類并排序后依次執(zhí)行
例子
寫了一個例子用來驗證分析的啟動邏輯,包括自定義的初始化器、監(jiān)聽器、ApplicationRunner和CommandLineRunner。
作者:丶Format
鏈接:http://www.lxweimin.com/p/d51e1896a5f7
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。