Springboot的啟動原理詳細解讀
springboot主函數(shù)
springboot項目一般都是打包成jar包直接運行main方法啟動,當然也可以跟傳統(tǒng)的項目一樣打包war包放在tomcat里面啟動.那么springboot怎么直接通過main方法啟動呢?
舉個栗子,這是一個簡單的main方法啟動類:
@EnableAsync @EnableScheduling @EnableTransactionManagement @EnableConfigurationProperties @EnableCaching @MapperScan(value = {"com.study.springbootplus.**.mapper"}) @SpringBootApplication public class SpringBootPlusApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args); } }
main方法
main方法主要就是看SpringApplication的run方法,這個方法大概就是創(chuàng)建個spring容器,然后創(chuàng)建個web容器(tomcat,jetty等)啟動.
run方法點進去,這里一個新建springApplication實例,一個run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
初始化SpringApplication實例
源碼:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // main方法中的args參數(shù),可接收命令行啟動時添加的參數(shù) this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 確認spring容器類型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //加載ApplicationContextInitializer類,ApplicationContext初始化類 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //加載ApplicationListener類,監(jiān)聽類 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //獲取main方法所在類 this.mainApplicationClass = deduceMainApplicationClass(); } private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
SpringApplication.run方法
源碼:
public ConfigurableApplicationContext run(String... args) { //計時 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //spring容器 ConfigurableApplicationContext context = null; //錯誤回調(diào) Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //設(shè)置一些系統(tǒng)屬性 configureHeadlessProperty(); //獲取啟動時監(jiān)聽器 SpringApplicationRunListeners listeners = getRunListeners(args); //啟動監(jiān)聽器 listeners.starting(); try { //獲取一些啟動參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //創(chuàng)建運行環(huán)境environment ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //設(shè)置一些系統(tǒng)參數(shù) configureIgnoreBeanInfo(environment); //打印banner Banner printedBanner = printBanner(environment); //創(chuàng)建spring容器 context = createApplicationContext(); //獲取異常報告,回調(diào) exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //準備容器 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新容器 refreshContext(context); //spring容器后置處理 afterRefresh(context, applicationArguments); //計時終止 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //結(jié)束通知 listeners.started(context); //執(zhí)行runner callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //spring容器就緒通知 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //返回容器 return context; }
getRunListeners方法,starting方法,獲取啟動監(jiān)聽,和啟動
源碼:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
前面講過啟動的時候會先加載spring會加載所有jar包下的META-INF/spring.factories,然后緩存起來,這里就獲取
返回spirngboot唯一實現(xiàn)SpringApplicationRunListener的接口:EventPublishingRunListener,然后執(zhí)行starting方法;starting方法就是構(gòu)建ApplicationStartingEvent,然后獲取listener執(zhí)行,這一塊其實還比較復(fù)雜.用到了適配器模式:
prepareEnvironment準備環(huán)境
//準備啟動參數(shù)就不說了 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //創(chuàng)建一個environment對象 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置環(huán)境 PropertySources Profiles configureEnvironment(environment, applicationArguments.getSourceArgs()); //PropertySources ConfigurationPropertySources.attach(environment); //環(huán)境準備 listeners.environmentPrepared(environment); //將環(huán)境綁定到oSpringApplication( bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } //配置PropertySources-如果有attach到environment ConfigurationPropertySources.attach(environment); return environment; }
createApplicationContext創(chuàng)建容器
protected ConfigurableApplicationContext createApplicationContext() { //獲取上下文的類 Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { //判斷Web應(yīng)用類型 case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
prepareContext,準備容器
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //設(shè)置環(huán)境 context.setEnvironment(environment); //處理上下文 postProcessApplicationContext(context); //獲取所有ApplicationContextInitializer,執(zhí)行ApplicationContextInitializer的init方法 applyInitializers(context); //調(diào)用SpringApplicationRunListener的contextPrepared,表示容器已經(jīng)準備 listeners.contextPrepared(context); //日志 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans //獲取beanfactory,注冊相關(guān)bean 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"); //加載啟動類 load(context, sources.toArray(new Object[0])); //調(diào)用SpringApplicationRunListener的contextPrepared,表示容器已經(jīng)loaded事件 listeners.contextLoaded(context); }
refreshContext,afterRefresh刷新容器,刷新容器之后執(zhí)行方法
就是spring容器的刷新方法
到此這篇關(guān)于Springboot的啟動原理詳細解讀的文章就介紹到這了,更多相關(guān)Springboot啟動原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring的BeanUtils.copyProperties屬性復(fù)制避坑指南
這篇文章主要介紹了Spring的BeanUtils.copyProperties屬性復(fù)制避坑指南,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08在MyBatis的XML映射文件中<trim>元素所有場景下的完整使用示例代碼
在MyBatis的XML映射文件中,<trim>元素用于動態(tài)添加SQL語句的一部分,處理前綴、后綴及多余的逗號或連接符,示例展示了如何在UPDATE、SELECT、INSERT和SQL片段中使用<trim>元素,以實現(xiàn)動態(tài)的SQL構(gòu)建,感興趣的朋友一起看看吧2025-01-01SpringBoot統(tǒng)一功能處理示例詳解(攔截器)
這篇文章主要介紹了SpringBoot統(tǒng)一功能處理(攔截器),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08Spring在多線程環(huán)境下如何確保事務(wù)一致性問題詳解
這篇文章主要介紹了Spring在多線程環(huán)境下如何確保事務(wù)一致性問題詳解,說到異步執(zhí)行,很多小伙伴首先想到Spring中提供的@Async注解,但是Spring提供的異步執(zhí)行任務(wù)能力并不足以解決我們當前的需求,需要的朋友可以參考下2023-11-11