SpringBoot應(yīng)用啟動(dòng)過(guò)程分析
SpringBoot項(xiàng)目通過(guò)SpringApplication.run(App.class, args)來(lái)啟動(dòng):
@Configuration
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
接下來(lái),通過(guò)源碼來(lái)看看SpringApplication.run()方法的執(zhí)行過(guò)程。如果對(duì)源碼不感興趣,直接下拉到文章末尾,看啟動(dòng)框圖。
1、調(diào)用SpringApplication類(lèi)的靜態(tài)方法
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
2、SpringApplication對(duì)象初始化
public SpringApplication(Object... sources) {
initialize(sources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判斷是否為WEB環(huán)境
this.webEnvironment = deduceWebEnvironment();
// 找到META-INF/spring.factories中ApplicationContextInitializer所有實(shí)現(xiàn)類(lèi),并將其實(shí)例化
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 找到META-INF/spring.factories中ApplicationListener所有實(shí)現(xiàn)類(lèi),并將其實(shí)例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 獲取當(dāng)前main方法類(lèi)對(duì)象,即測(cè)試類(lèi)中的App實(shí)例
this.mainApplicationClass = deduceMainApplicationClass();
}
對(duì)象初始化過(guò)程中,使用到了getSpringFactoriesInstances方法:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
// 讀取META-INF/spring.factories指定接口的實(shí)現(xiàn)類(lèi)
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
T instance = (T) constructor.newInstance(args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
// 讀取META-INF/spring.factories文件
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
META-INF/spring.factories文件內(nèi)容,spring boot版本1.3.6.RELEASE # PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
ApplicationListener接口是Spring框架的事件監(jiān)聽(tīng)器,其作用可理解為SpringApplicationRunListener發(fā)布通知事件時(shí),由ApplicationListener負(fù)責(zé)接收。SpringApplicationRunListener接口的實(shí)現(xiàn)類(lèi)就是EventPublishingRunListener,其在SpringBoot啟動(dòng)過(guò)程中,負(fù)責(zé)注冊(cè)ApplicationListener監(jiān)聽(tīng)器,在不同時(shí)間節(jié)點(diǎn)發(fā)布不同事件類(lèi)型,如果有ApplicationListener實(shí)現(xiàn)類(lèi)監(jiān)聽(tīng)了該事件,則接收處理。
public interface SpringApplicationRunListener {
/**
* 通知監(jiān)聽(tīng)器,SpringBoot開(kāi)始啟動(dòng)
*/
void started();
/**
* 通知監(jiān)聽(tīng)器,環(huán)境配置完成
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* 通知監(jiān)聽(tīng)器,ApplicationContext已創(chuàng)建并初始化完成
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* 通知監(jiān)聽(tīng)器,ApplicationContext已完成IOC配置
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* 通知監(jiān)聽(tīng)器,SpringBoot開(kāi)始完畢
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}
附圖為ApplicationListener監(jiān)聽(tīng)接口實(shí)現(xiàn)類(lèi),每個(gè)類(lèi)對(duì)應(yīng)了一種事件。

3、SpringApplication核心run方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 任務(wù)執(zhí)行時(shí)間監(jiān)聽(tīng),記錄起止時(shí)間差
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 啟動(dòng)SpringApplicationRunListener監(jiān)聽(tīng)器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 創(chuàng)建并刷新ApplicationContext
context = createAndRefreshContext(listeners, applicationArguments);
afterRefresh(context, applicationArguments);
// 通知監(jiān)聽(tīng)器,應(yīng)用啟動(dòng)完畢
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
throw new IllegalStateException(ex);
}
}
這里,需要看看createAndRefreshContext()方法是如何創(chuàng)建并刷新ApplicationContext。
private ConfigurableApplicationContext createAndRefreshContext(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableApplicationContext context;
// Create and configure the environment
// 創(chuàng)建并配置運(yùn)行環(huán)境,WebEnvironment與StandardEnvironment選其一
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
// 是否打印Banner,就是啟動(dòng)程序時(shí)出現(xiàn)的圖形
if (this.bannerMode != Banner.Mode.OFF) {
printBanner(environment);
}
// Create, load, refresh and run the ApplicationContext
// 創(chuàng)建、裝置、刷新、運(yùn)行ApplicationContext
context = createApplicationContext();
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
// 通知監(jiān)聽(tīng)器,ApplicationContext創(chuàng)建完畢
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
// Load the sources
// 將beans載入到ApplicationContext容器中
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
// 通知監(jiān)聽(tīng)器,beans載入ApplicationContext完畢
listeners.contextLoaded(context);
// Refresh the context
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
return context;
}
其中利用createApplicationContext()來(lái)實(shí)例化ApplicationContext對(duì)象,即DEFAULT_WEB_CONTEXT_CLASS 、DEFAULT_CONTEXT_CLASS兩個(gè)對(duì)象其中一個(gè)。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
postProcessApplicationContext(context)、applyInitializers(context)均為初始化ApplicationContext工作。
SpringBoot啟動(dòng)過(guò)程分析就先到這里,過(guò)程中關(guān)注幾個(gè)對(duì)象:
ApplicationContext:Spring高級(jí)容器,與BeanFactory類(lèi)似。
SpringApplicationRunListener:SprintBoot啟動(dòng)監(jiān)聽(tīng)器,負(fù)責(zé)向ApplicationListener注冊(cè)各類(lèi)事件。
Environment:運(yùn)行環(huán)境。
4、啟動(dòng)過(guò)程框圖

5、接口文檔
http://docs.spring.io/spring-framework/docs/current/javadoc-api/
總結(jié)
以上所述是小編給大家介紹的SpringBoot應(yīng)用啟動(dòng)過(guò)程分析,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Spring中@Value讀取properties作為map或list的操作
這篇文章主要介紹了Spring中@Value讀取properties作為map或list的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java 數(shù)據(jù)庫(kù)連接池c3p0 介紹
這篇文章主要介給大家分享了 Java 數(shù)據(jù)庫(kù)連接池c3p0 介紹,c3p0 是一個(gè)成熟的、高并發(fā)的 JDBC 連接池庫(kù),支持緩存和 PreparedStatements 的重用。它以LGPL v.2.1或EPL v.1.0授權(quán),下面我們就一起來(lái)看看文章內(nèi)容的詳細(xì)介紹吧,需要的朋友也可以參考一下2021-11-11
SpringBoot中LogBack日志配置與多環(huán)境實(shí)戰(zhàn)
在現(xiàn)代軟件開(kāi)發(fā)中,日志記錄是不可或缺的一部分,Spring Boot 提供了多種日志框架的支持,其中 Logback 是一個(gè)非常流行的選擇,因?yàn)樗?jiǎn)單、高效且功能強(qiáng)大,本文將介紹如何在 Spring Boot 項(xiàng)目中配置 Logback,并實(shí)現(xiàn)不同環(huán)境下的日志配置,需要的朋友可以參考下2025-01-01
解讀nextLine().split(“[\\s]“)的意思
這篇文章主要介紹了解讀nextLine().split(“[\\s]“)的意思,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
java開(kāi)發(fā)RocketMQ生產(chǎn)者高可用示例詳解
這篇文章主要為大家介紹了java開(kāi)發(fā)RocketMQ生產(chǎn)者高可用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Spring?Boot?使用?Hutool-jwt?實(shí)現(xiàn)?token?驗(yàn)證功能
JWT?就是一種網(wǎng)絡(luò)身份認(rèn)證和信息交換格式,這篇文章主要介紹了Spring Boot使用Hutool-jwt實(shí)現(xiàn)token驗(yàn)證,需要的朋友可以參考下2023-07-07

