Springboot的啟動(dòng)原理詳細(xì)解讀
springboot主函數(shù)
springboot項(xiàng)目一般都是打包成jar包直接運(yùn)行main方法啟動(dòng),當(dāng)然也可以跟傳統(tǒng)的項(xiàng)目一樣打包war包放在tomcat里面啟動(dòng).那么springboot怎么直接通過(guò)main方法啟動(dòng)呢?
舉個(gè)栗子,這是一個(gè)簡(jiǎn)單的main方法啟動(dòng)類:
@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方法,這個(gè)方法大概就是創(chuàng)建個(gè)spring容器,然后創(chuàng)建個(gè)web容器(tomcat,jetty等)啟動(dòng).
run方法點(diǎn)進(jìn)去,這里一個(gè)新建springApplication實(shí)例,一個(gè)run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
初始化SpringApplication實(shí)例
源碼:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// main方法中的args參數(shù),可接收命令行啟動(dòng)時(shí)添加的參數(shù)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 確認(rèn)spring容器類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加載ApplicationContextInitializer類,ApplicationContext初始化類
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加載ApplicationListener類,監(jiān)聽(tīng)類
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) {
//計(jì)時(shí)
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//spring容器
ConfigurableApplicationContext context = null;
//錯(cuò)誤回調(diào)
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//設(shè)置一些系統(tǒng)屬性
configureHeadlessProperty();
//獲取啟動(dòng)時(shí)監(jiān)聽(tīng)器
SpringApplicationRunListeners listeners = getRunListeners(args);
//啟動(dòng)監(jiān)聽(tīng)器
listeners.starting();
try {
//獲取一些啟動(dòng)參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//創(chuàng)建運(yùn)行環(huán)境environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//設(shè)置一些系統(tǒng)參數(shù)
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//創(chuàng)建spring容器
context = createApplicationContext();
//獲取異常報(bào)告,回調(diào)
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//準(zhǔn)備容器
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器
refreshContext(context);
//spring容器后置處理
afterRefresh(context, applicationArguments);
//計(jì)時(shí)終止
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方法,獲取啟動(dòng)監(jiān)聽(tīng),和啟動(dòng)
源碼:
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;
}
前面講過(guò)啟動(dòng)的時(shí)候會(huì)先加載spring會(huì)加載所有jar包下的META-INF/spring.factories,然后緩存起來(lái),這里就獲取

返回spirngboot唯一實(shí)現(xiàn)SpringApplicationRunListener的接口:EventPublishingRunListener,然后執(zhí)行starting方法;starting方法就是構(gòu)建ApplicationStartingEvent,然后獲取listener執(zhí)行,這一塊其實(shí)還比較復(fù)雜.用到了適配器模式:

prepareEnvironment準(zhǔn)備環(huán)境
//準(zhǔn)備啟動(dòng)參數(shù)就不說(shuō)了
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//創(chuàng)建一個(gè)environment對(duì)象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置環(huán)境 PropertySources Profiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
//PropertySources
ConfigurationPropertySources.attach(environment);
//環(huán)境準(zhǔ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,準(zhǔn)備容器
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)準(zhǔn)備
listeners.contextPrepared(context);
//日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//獲取beanfactory,注冊(cè)相關(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");
//加載啟動(dòng)類
load(context, sources.toArray(new Object[0]));
//調(diào)用SpringApplicationRunListener的contextPrepared,表示容器已經(jīng)loaded事件
listeners.contextLoaded(context);
}
refreshContext,afterRefresh刷新容器,刷新容器之后執(zhí)行方法
就是spring容器的刷新方法
到此這篇關(guān)于Springboot的啟動(dòng)原理詳細(xì)解讀的文章就介紹到這了,更多相關(guān)Springboot啟動(dòng)原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成極光推送的實(shí)現(xiàn)代碼
工作中經(jīng)常會(huì)遇到服務(wù)器向App推送消息的需求,一般企業(yè)中選擇用極光推送的比較多,本文就介紹了SpringBoot集成極光推送的實(shí)現(xiàn)代碼,感興趣的可以了解一下2023-08-08
Spring的BeanUtils.copyProperties屬性復(fù)制避坑指南
這篇文章主要介紹了Spring的BeanUtils.copyProperties屬性復(fù)制避坑指南,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題
這篇文章主要介紹了Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10
在MyBatis的XML映射文件中<trim>元素所有場(chǎng)景下的完整使用示例代碼
在MyBatis的XML映射文件中,<trim>元素用于動(dòng)態(tài)添加SQL語(yǔ)句的一部分,處理前綴、后綴及多余的逗號(hào)或連接符,示例展示了如何在UPDATE、SELECT、INSERT和SQL片段中使用<trim>元素,以實(shí)現(xiàn)動(dòng)態(tài)的SQL構(gòu)建,感興趣的朋友一起看看吧2025-01-01
SpringBoot統(tǒng)一功能處理示例詳解(攔截器)
這篇文章主要介紹了SpringBoot統(tǒng)一功能處理(攔截器),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
Spring在多線程環(huán)境下如何確保事務(wù)一致性問(wèn)題詳解
這篇文章主要介紹了Spring在多線程環(huán)境下如何確保事務(wù)一致性問(wèn)題詳解,說(shuō)到異步執(zhí)行,很多小伙伴首先想到Spring中提供的@Async注解,但是Spring提供的異步執(zhí)行任務(wù)能力并不足以解決我們當(dāng)前的需求,需要的朋友可以參考下2023-11-11

