SpringBoot啟動流程
整個啟動流程包含,推斷 WEB 應用類型,設置初始化器,設置 ApplicationListener 監聽器,獲取並啟動 SpringApplicationRunListener 類,准備 Spring 環境,創建並執行 banner 打印類,創建應用上下文,准備應用上下文,刷新應用上下文,刷新應用上下文之后的調用,執行所有的 Runner 運行器。
Spring Boot 的入口程序
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
創建 SpringApplication 對象
- 初始化主要加載資源類到集合中
- 推斷當前 WEB 應用類型(NONE、SERVLET、REACTIVE)
- 設置應用上下文初始化器
- 設置 ApplicationListener 監聽器
- 推斷主入口應用類
// 創建一個新的實例,這個應用上下文將從指定的來源加載 Bean
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 初始化資源加載器,默認 null
this.resourceLoader = resourceLoader;
// 斷言主資源不能為空
Assert.notNull(primarySources, "PrimarySources must not be null");
// 初始化主要加載資源類集合並去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推斷當前 WEB 應用類型,NONE、SERVLET、REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加載引導程序(2.4.0增加的功能)
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 設置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 設置監聽器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推斷主入口應用類
this.mainApplicationClass = deduceMainApplicationClass();
}
run 方法啟動
- 創建引導上下文
- 獲取並啟動所有 SpringApplicationRunListener 對象
- 創建默認的應用參數類
- 准備 Spring 應用環境
- 創建並執行 banner 打印類
- 創建應用上下文
- 准備應用上下文
- 刷新應用上下文
public ConfigurableApplicationContext run(String... args) {
// 創建並啟動計時監控類
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 創建引導上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 設置系統屬性“java.awt.headless”的值,默認為 true
configureHeadlessProperty();
// 創建所有 Spring 運行監聽器並發布應用啟動事件,
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 初始化默認應用參數類
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根據運行監聽器、引導上下文、應用參數來准備 Spring 環境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 將要忽略的 bean 參數打開
configureIgnoreBeanInfo(environment);
// 創建並執行 banner 打印類
Banner printedBanner = printBanner(environment);
// 創建應用上下文
context = createApplicationContext();
// 設置應用程序啟動 步驟
context.setApplicationStartup(this.applicationStartup);
// 准備應用上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新應用上下文
refreshContext(context);
// 刷新之后,空方法
afterRefresh(context, applicationArguments);
// 停止計時監控類
stopWatch.stop();
// 輸出日志記錄
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 發布應用上下文啟動監聽事件
listeners.started(context);
// 執行所有的 Runner 運行器
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 發布應用上下文就緒事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
創建引導上下文
創建並啟動引導上下文,2.4.0 之后的版本增加的新功能
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
return bootstrapContext;
}
獲取所有 SpringApplicationRunListener 對象並啟動
// 從 META-INF/spring.factories 中獲取所有的配置類,並將其封裝到 SpringApplicationRunListeners 對象中去,主要創建的對象為 EventPublishingRunListener
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
// 廣播事件
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
// applicationStartingEvent 是 SpringBoot 框架最早執行的監聽器,在該監聽器執行 started 方法時,會繼續發布事件,主要是基於 Spring 的事件機制
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 如果線程池為空,則同步發送事件,否則異步發送事件
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
// 返回與給定事件匹配的監聽器
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Potential new retriever to populate
CachedListenerRetriever newRetriever = null;
// Quick check for existing entry on ConcurrentHashMap
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever == null) {
// Caching a new ListenerRetriever if possible
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
newRetriever = new CachedListenerRetriever();
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
newRetriever = null; // no need to populate it in retrieveApplicationListeners
}
}
}
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
// If result is null, the existing retriever is not fully populated yet by another thread.
// Proceed like caching wasn't possible for this current local attempt.
}
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
創建默認的應用參數類
Source 繼承自 SimpleCommandLinePropertySource,用於解析簡單的命令行參數
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
准備 Spring 應用環境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 創建或者獲取應用環境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置應用環境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// listeners 環境准備,廣播 ApplicationEnvironmentPreparedEvent
listeners.environmentPrepared(bootstrapContext, environment);
// 附加配置源
DefaultPropertiesPropertySource.moveToEnd(environment);
// 配置其他激活文件
configureAdditionalProfiles(environment);
// 將環境綁定給當前應用程序
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 配置 propertySource 對它自己的遞歸依賴
ConfigurationPropertySources.attach(environment);
return environment;
}
創建 banner 打印類並執行
打印 banner 的詳細操作過程
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
創建應用上下文
根據不同的應用類型初始化不同的山下文應用類
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
准備應用上下文
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 設置應用上下文的 environment
context.setEnvironment(environment);
// 應用山下文的后置處理
postProcessApplicationContext(context);
// 為上下文應用所有初始化器,執行前面設置的 ApplicationContextInitializer 類
applyInitializers(context);
// 觸發所有 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法。添加所有的事件監聽器
listeners.contextPrepared(context);
bootstrapContext.close(context);
// 記錄啟動日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 注冊啟動參數bean,將容器指定的參數封裝成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");
// 將 bean 加載到上下文中
load(context, sources.toArray(new Object[0]));
// 觸發所有 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
// 后續會使用其將主資源加載到上下文中去,供后續解析使用
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
void load() {
for (Object source : this.sources) {
load(source);
}
}
private void load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
// Class 的方式加載
load((Class<?>) source);
return;
}
if (source instanceof Resource) {
// Resource 資源的方式加載
load((Resource) source);
return;
}
if (source instanceof Package) {
load((Package) source);
return;
}
if (source instanceof CharSequence) {
load((CharSequence) source);
return;
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
private void load(Class<?> source) {
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
}
if (isEligible(source)) {
// 將符合條件的類注冊到應用上下文
this.annotatedReader.register(source);
}
}
// 不是常規的閉包並且不是匿名類,即可
private boolean isEligible(Class<?> type) {
return !(type.isAnonymousClass() || isGroovyClosure(type) || hasNoConstructors(type));
}
刷新應用上下文
最終會調用到 AbstractApplicationContext 抽象類中的 refresh() 方法中去
refreshContext(context);
刷新后應用上下文調用
空方法,用於擴展
執行所有的 Runner 運行器
執行所有 Runner 執行器,執行所有 ApplicationRunner 和 CommandLineRunner 兩種運行器。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}