您好,登錄后才能下訂單哦!
Springboot Code的啟動源碼是怎樣的,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
項目啟動的流程:
(一)new SpringApplication
配置source和web環境;
創建初始化構造器和應用監聽器;
配置應用的主方法所在類;
(二)run 第一二部分
1.獲取并啟動監聽器
初始化計時stopWatch、啟動上下文bootstrapContext、設置系統參數headless;
初始化監聽器列表SpringApplicationRunListeners;
發布springboot開始啟動事件(從applicationListeners中過濾出4個能監聽ApplicationStartingEvent事件的,并啟動它們)
2.準備環境
裝配命令行參數applicationArguments(對象中裝載4個propertySource);
準備應用程序運行的環境ConfigurableEnvironment(從applicationListeners中過濾出6個能監聽ApplicationEnvironmentPreparedEvent事件的,并啟動它們。監聽器中關聯啟動了一些后置處理器處理數據,最終目的是為應用環境做準備)
3.打印banner
public ConfigurableApplicationContext run(String... args) { //1、StopWatch簡單的看成一個stop watch的機制,保存stop的記錄信息。 //初始化一個計時器,并開始計時 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //初始化啟動上下文 DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); ConfigurableApplicationContext context = null; //2、configureHeadlessProperty即配置headless模式,這種模式是一種系統缺少顯示設備、鍵盤和鼠標外設的情況模式。 this.configureHeadlessProperty(); //3、SpringApplicationListeners為SpringApplicationRunListener接口實現集合(創建SpringApplicationRunListener初始化構造器)初始化監聽器列表 //可以理解這個接口就是在spring啟動整個過程都需要回調這些listener //debug能發現,拿到了一個名為EventPublishingRunListener(RunListener構造方法中關聯上了全部applicationListener),這個就是用來進行觸發publishEvent的被觀察者 SpringApplicationRunListeners listeners = this.getRunListeners(args); //啟動EventPublishingRunListener,從而過濾并啟動相關Listener listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //4、ConfigurableEnvironment為配置環境對象,簡單理解所有的配置信息匯總在這個對象中 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); //5、Banner就是我們常在控制臺輸出的畫面橫幅,可以使用圖片或者文本進行替換 Banner printedBanner = this.printBanner(environment); =====> //6、ConfigurableApplicationContext根據webApp…Type進行構造的上下文對象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執行所有的initializers邏輯,做初始化準備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //8、refreshContext,可以理解成容器初始化節點,將執行bean的創建和實例化。 this.refreshContext(context); //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現類并執行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。 this.afterRefresh(context, applicationArguments); //10、然后根據stopwatch打印出啟動時間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調用ApplicationRunner和CommandLineRunner的實現類 this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
下面開始對context的創建與準備
context = this.createApplicationContext(); protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); }
public interface ApplicationContextFactory { ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch(webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } } catch (Exception var2) { throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2); } }; ConfigurableApplicationContext create(WebApplicationType webApplicationType); .... }
上述代碼可以通過webApplicationTyep(即SERVLET)創建 AnnotationConfigServletWebServerApplicationContext 對象,對象創建過程中,初始化了父類的屬性值,其中有三個比較關鍵的值,reader、scanner和父類中的beanFactory,下面這個對象的層級結構需要了解一下
/** * reader 和 scanner 都是在構造方法中進行了賦值 **/ public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set<Class<?>> annotatedClasses; private String[] basePackages; public AnnotationConfigServletWebServerApplicationContext() { this.annotatedClasses = new LinkedHashSet(); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } ... }
我們可以在 GenericApplicationContext 類的構造方法中看到其構造方法中,對beanFactory屬性的賦值
public GenericApplicationContext() { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); this.beanFactory = new DefaultListableBeanFactory(); }
//6、ConfigurableApplicationContext根據webApp…Type進行構造的上下文對象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); =====> //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執行所有的initializers邏輯,做初始化準備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //8、refreshContext,可以理解成容器初始化節點,將執行bean的創建和實例化。 this.refreshContext(context); //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現類并執行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。 this.afterRefresh(context, applicationArguments); //10、然后根據stopwatch打印出啟動時間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調用ApplicationRunner和CommandLineRunner的實現類 this.callRunners(context, applicationArguments);
/** * Spring容器準備 */ private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 設置上下文環境 context.setEnvironment(environment); // postProcessApplicationContext(context); // 執行所有ApplicationContextInitializer對象的initialize方法(這些對象是通過讀取spring.factories加載) applyInitializers(context); // 發布上下文準備完成事件到所有監聽器 listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加載bean到上下文 load(context, sources.toArray(new Object[0])); // 發送上下文加載完成事件 listeners.contextLoaded(context); } //為context設置beanFactoryPostProcess protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader()); } } if (this.addConversionService) { context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); } } //調用全部的構造器 protected void applyInitializers(ConfigurableApplicationContext context) { Iterator var2 = this.getInitializers().iterator(); while(var2.hasNext()) { ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next(); Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } } //加載class com.learning.demo.DemoApplication 啟動類 protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); }
debug,此處load方法可以跟進多層到 AnnotatedBeanDefinitionReader 的 registerBean(啟動類)
public void registerBean(Class<?> beanClass) { this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null); }
這里同樣有兩個listeners.*的兩個方法,和前面listeners.starting()是一樣的,提供下最近整理的類圖(了解Runlistener和listener類之間的關系)
//6、ConfigurableApplicationContext根據webApp…Type進行構造的上下文對象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執行所有的initializers邏輯,做初始化準備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); =====> //8、refreshContext,可以理解成容器初始化節點,將執行bean的創建和實例化。 this.refreshContext(context); //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現類并執行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。 this.afterRefresh(context, applicationArguments); //10、然后根據stopwatch打印出啟動時間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調用ApplicationRunner和CommandLineRunner的實現類 this.callRunners(context, applicationArguments);
refreshContext() 是整個Run方法的核心部分
/** * 刷新應用程序上下文 * * @param context */ private void refreshContext(ConfigurableApplicationContext context) { // 注冊一個關閉鉤子,在jvm停止時會觸發,然后退出時執行一定的退出邏輯 if (this.registerShutdownHook) { try { // 添加:Runtime.getRuntime().addShutdownHook() // 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook) context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } // ApplicationContext真正開始初始化容器和創建bean的階段 refresh((ApplicationContext) context); } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); refresh((ConfigurableApplicationContext) applicationContext); } protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); }
調用應用上下文對象的refresh()方法,接下來我i門到ConfigurableApplicationContext類中去看下這個方法
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { void refresh() throws BeansException, IllegalStateException; }
這是一個接口,且這個類是在spring框架中,非springboot,它的實現類共有三個
AbstractApplicationContext是一個抽象類,其余兩個類都繼承了它,我們來看看這個抽象類的代碼:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 第一步:準備更新上下時的預備工作 prepareRefresh(); // 第二步:獲取上下文內部BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 第三步:對BeanFactory做預備工作 prepareBeanFactory(beanFactory); try { // 第四步:允許在上下文子類中對bean工廠進行post-processing postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // 第五步:調用上下文中注冊為bean的工廠 BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 第六步:注冊攔截bean創建的攔截器 registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // 第七步:初始化MessageSource(國際化相關) initMessageSource(); // 第八步:初始化容器事件廣播器(用來發布事件) initApplicationEventMulticaster(); // 第九步:初始化一些特殊的bean onRefresh(); // 第十步:將所有監聽器注冊到前兩步創建的事件廣播器中 registerListeners(); // 第十一步:結束bean的初始化工作(主要將所有單例BeanDefinition實例化) finishBeanFactoryInitialization(beanFactory); // 第十二步:afterRefresh(上下文刷新完畢,發布相應事件) finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); contextRefresh.end(); } } }
這里有非常多的步驟,上下文對象主要的bean也是在這里進行處理的,具體的說明可以看注釋, fresh方法就是SpringFrameWork的那部分(不再細化)
//6、ConfigurableApplicationContext根據webApp…Type進行構造的上下文對象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執行所有的initializers邏輯,做初始化準備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //8、refreshContext,可以理解成容器初始化節點,將執行bean的創建和實例化。 this.refreshContext(context); =====> //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現類并執行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。 this.afterRefresh(context, applicationArguments); =====> //10、然后根據stopwatch打印出啟動時間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調用ApplicationRunner和CommandLineRunner的實現類 this.callRunners(context, applicationArguments);
afterRefresh() 是對方法 refresh() 的擴展,暫時空方法。
stopWatch.stop() 根據stopwatch打印出啟動時間,至此項目已經啟動完成。
* run方法主要做如下幾件事情:
發出啟動結束事件
執行實現ApplicationRunner、CommandLineRunner的run方法
發布應用程序已啟動(ApplicationStartedEvent)事件
結合網上共享的兩張圖可以清晰回顧下整體流程:
關于Springboot Code的啟動源碼是怎樣的問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。