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));
// 獲取當前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負責(zé)接收。SpringApplicationRunListener接口的實現(xiàn)類就是EventPublishingRunListener,其在SpringBoot啟動過程中,負責(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)聽器,負責(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)文章
Java編程一個隨機數(shù)產(chǎn)生模塊代碼分享
這篇文章主要介紹了Java編程一個隨機數(shù)產(chǎn)生模塊代碼分享,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12
Java前端開發(fā)之HttpServletRequest的使用
service方法中的request的類型是ServletRequest,而doGet/doPost方法的request的類型是HttpServletRequest,HttpServletRequest是ServletRequest的子接口,功能和方法更加強大2023-01-01
Spring Boot 中application.yml與bootstrap.yml的區(qū)別
其實yml和properties文件是一樣的原理,且一個項目上要么yml或者properties,二選一的存在。這篇文章給大家介紹了Spring Boot 中application.yml與bootstrap.yml的區(qū)別,感興趣的朋友一起看看吧2018-04-04
深入探究Java中的HashMap為什么會產(chǎn)生死循環(huán)
HashMap?死循環(huán)發(fā)生在?JDK?1.8?之前的版本中,這篇文章主要來和大家深入探究一下為什么Java中HashMap會產(chǎn)生死循環(huán),感興趣的小伙伴可以了解一下2023-05-05
kafka生產(chǎn)者和消費者的javaAPI的示例代碼
這篇文章主要介紹了kafka生產(chǎn)者和消費者的javaAPI的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06

