SpringBoot應(yīng)用啟動過程分析
SpringBoot項目通過SpringApplication.run(App.class, args)來啟動:
@Configuration
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
接下來,通過源碼來看看SpringApplication.run()方法的執(zhí)行過程。如果對源碼不感興趣,直接下拉到文章末尾,看啟動框圖。
1、調(diào)用SpringApplication類的靜態(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對象初始化
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所有實現(xiàn)類,并將其實例化
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 找到META-INF/spring.factories中ApplicationListener所有實現(xiàn)類,并將其實例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 獲取當(dāng)前main方法類對象,即測試類中的App實例
this.mainApplicationClass = deduceMainApplicationClass();
}
對象初始化過程中,使用到了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指定接口的實現(xiàn)類
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)聽器,其作用可理解為SpringApplicationRunListener發(fā)布通知事件時,由ApplicationListener負(fù)責(zé)接收。SpringApplicationRunListener接口的實現(xiàn)類就是EventPublishingRunListener,其在SpringBoot啟動過程中,負(fù)責(zé)注冊ApplicationListener監(jiān)聽器,在不同時間節(jié)點發(fā)布不同事件類型,如果有ApplicationListener實現(xiàn)類監(jiān)聽了該事件,則接收處理。
public interface SpringApplicationRunListener {
/**
* 通知監(jiān)聽器,SpringBoot開始啟動
*/
void started();
/**
* 通知監(jiān)聽器,環(huán)境配置完成
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* 通知監(jiān)聽器,ApplicationContext已創(chuàng)建并初始化完成
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* 通知監(jiān)聽器,ApplicationContext已完成IOC配置
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* 通知監(jiān)聽器,SpringBoot開始完畢
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}
附圖為ApplicationListener監(jiān)聽接口實現(xiàn)類,每個類對應(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í)行時間監(jiān)聽,記錄起止時間差
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 啟動SpringApplicationRunListener監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 創(chuàng)建并刷新ApplicationContext
context = createAndRefreshContext(listeners, applicationArguments);
afterRefresh(context, applicationArguments);
// 通知監(jiān)聽器,應(yī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)建并配置運行環(huán)境,WebEnvironment與StandardEnvironment選其一
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
// 是否打印Banner,就是啟動程序時出現(xiàn)的圖形
if (this.bannerMode != Banner.Mode.OFF) {
printBanner(environment);
}
// Create, load, refresh and run the ApplicationContext
// 創(chuàng)建、裝置、刷新、運行ApplicationContext
context = createApplicationContext();
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
// 通知監(jiān)聽器,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)聽器,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()來實例化ApplicationContext對象,即DEFAULT_WEB_CONTEXT_CLASS 、DEFAULT_CONTEXT_CLASS兩個對象其中一個。
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啟動過程分析就先到這里,過程中關(guān)注幾個對象:
ApplicationContext:Spring高級容器,與BeanFactory類似。
SpringApplicationRunListener:SprintBoot啟動監(jiān)聽器,負(fù)責(zé)向ApplicationListener注冊各類事件。
Environment:運行環(huán)境。
4、啟動過程框圖

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

