首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring Boot 啟動原始碼深度解析(二):run() 方法完整執行流程與核心階段

     編輯:Java 綜合教學

Spring Boot 啟動原始碼深度解析(二):run() 方法完整執行流程

承接上篇 SpringApplication 初始化,本文深度剖析 run() 方法的 10 大核心階段,從環境準備到上下文刷新,完整呈現 Spring Boot 啟動生命週期。

一、run() 方法總覽

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    
    // 1. 獲取事件發布器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();  // 發布 ApplicationStartingEvent
    
    try {
        // 2. 核心執行流程
        context = doRun(listeners, args);
        
        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);
    }
}

二、doRun() 核心 10 階段詳細解析

階段 1:建立並配置 Environment

ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, args);
listeners.environmentPrepared(environment);  // 發布 ApplicationEnvironmentPreparedEvent

Environment 組成:

  • PropertySource:優先級遞減 —— 命令行參數 > ServletConfig/Context 參數 > JNDI > Java System Properties > OS 環境變數 > RandomValuePropertySource > application-{profile}.properties > application.properties > @PropertySource > SpringApplication.setDefaultProperties
  • Profiles:active profiles(spring.profiles.active)& default profiles

階段 2:轉換環境類型(Web/非 Web)

if (isWebEnvironment(environment) && !this.webEnvironment) {
    environment = convertToStandardEnvironment(environment);
}

階段 3:列印 Banner

if (this.bannerMode != Banner.Mode.OFF) {
    printBanner(environment);
}

階段 4:建立 ApplicationContext

context = createApplicationContext();

類型決定邏輯:

  • Web 環境 + Servlet 容器:AnnotationConfigServletWebServerApplicationContext
  • Web 環境 + Reactive:AnnotationConfigReactiveWebServerApplicationContext
  • 非 Web:AnnotationConfigApplicationContext

階段 5:準備上下文

context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);  // 執行所有 ApplicationContextInitializer.initialize()
listeners.contextPrepared(context);  // 發布 ApplicationContextPreparedEvent

階段 6:記錄啟動資訊

if (this.logStartupInfo) {
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
}

階段 7:註冊啟動參數 Bean

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);

階段 8:載入 Bean 定義(核心:自動配置進入點)

Set sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));

load() 核心邏輯:

protected void load(ApplicationContext context, Object[] sources) {
    BeanDefinitionLoader loader = new BeanDefinitionLoader(context);
    for (Object source : sources) {
        if (source instanceof Class) {
            loader.load((Class) source);
        } else if (source instanceof Resource) {
            loader.load((Resource) source);
        } else if (source instanceof Package) {
            loader.load((Package) source);
        } else if (source instanceof CharSequence) {
            loader.load((CharSequence) source);
        } else {
            throw new IllegalArgumentException("Invalid source type");
        }
    }
}

關鍵點:@SpringBootApplication → @EnableAutoConfiguration → AutoConfigurationImportSelector → 掃描 META-INF/spring.factories 中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有自動配置類 → 根據條件註解(@ConditionalOnClass、@ConditionalOnMissingBean 等)決定載入哪些 Bean 定義。

階段 9:發布上下文載入完成事件

listeners.contextLoaded(context);  // 發布 ApplicationPreparedEvent
// 此時所有 ApplicationListener 已註冊到 ApplicationContext

階段 10:刷新上下文(最關鍵、最耗時)

refresh(context);  // AbstractApplicationContext.refresh()

// 刷新核心子流程:
// 1. prepareRefresh() - 驗證、鎖定、啟動啟動
// 2. obtainFreshBeanFactory() - 建立/獲取 BeanFactory
// 3. prepareBeanFactory() - 配置 BeanFactory(後置處理器、忽略介面等)
// 4. postProcessBeanFactory() - 子類擴充點
// 5. invokeBeanFactoryPostProcessors() - 執行 BeanFactoryPostProcessor(ConfigurationClassPostProcessor 處理 @Configuration/@ComponentScan 等)
// 6. registerBeanPostProcessors() - 註冊 BeanPostProcessor
// 7. initMessageSource() - 國際化
// 8. initApplicationEventMulticaster() - 事件多播器
// 9. onRefresh() - 子類擴充(ServletWebServerApplicationContext 建立內嵌 Tomcat/Jetty/Undertow)
// 10. registerListeners() - 註冊 ApplicationListener
// 11. finishBeanFactoryInitialization() - 實例化所有非懶加載單例 Bean
// 12. finishRefresh() - 清理、發布 ContextRefreshedEvent、註冊 MBean

階段 11:註冊關閉鉤子

if (this.registerShutdownHook) {
    try {
        context.registerShutdownHook();  // JVM 關閉時優雅關閉上下文
    } catch (AccessControlException ex) {
        // 某些環境(如測試)不允許
    }
}

階段 12:刷新後處理與完成事件

afterRefresh(context, applicationArguments);
listeners.finished(context, null);  // 發布 ApplicationReadyEvent / ApplicationFailedEvent

三、關鍵事件發布時序圖

SpringApplication.run()
    │
    ├─▶ listeners.starting()
    │       └─▶ ApplicationStartingEvent
    │
    ├─▶ environmentPrepared()
    │       └─▶ ApplicationEnvironmentPreparedEvent
    │               ├─▶ ConfigFileApplicationListener → 載入 application.yml
    │               └─▶ LoggingApplicationListener → 初始化日誌系統
    │
    ├─▶ contextPrepared()
    │       └─▶ ApplicationContextPreparedEvent
    │
    ├─▶ contextLoaded()
    │       └─▶ ApplicationPreparedEvent
    │               ├─▶ 註冊所有 ApplicationListener 到 Context
    │               └─▶ 啟動內嵌 Servlet 容器(onRefresh 階段)
    │
    ├─▶ refresh() 
    │       └─▶ ContextRefreshedEvent (finishRefresh 階段)
    │
    └─▶ listeners.finished()
            └─▶ ApplicationReadyEvent (成功) / ApplicationFailedEvent (失敗)
                    ├─▶ 應用完全就緒,可處理請求
                    └─▶ 適合執行:健康檢查、緩存預熱、排程啟動

四、核心擴充點總結

擴充點介面/註解觸發時機典型用途
ApplicationContextInitializerinitialize(ConfigurableApplicationContext)contextPrepared 前動態註冊 PropertySource、配置初始 Bean
ApplicationListeneronApplicationEvent(Event)各階段事件發布時啟動監控、配置載入、日誌初始化
BeanFactoryPostProcessorpostProcessBeanFactory(BeanFactory)refresh() 階段 5掃描配置類、處理 @Value、動態註冊 BeanDefinition
BeanPostProcessorpostProcessBefore/AfterInitializationBean 初始化前後AOP 代理、依賴注入、驗證、修改 Bean
SmartInitializingSingletonafterSingletonsInstantiated()所有單例 Bean 建立後啟動後初始化任務、註冊監控指標
ApplicationRunner/CommandLineRunnerrun(args)finished() 事件後啟動時業務邏輯、資料遷移、緩存預熱

五、效能優化關鍵點

  • 條件註解過濾:@ConditionalOnClass、@ConditionalOnMissingBean 減少不必要的 Bean 定義載入
  • 懶加載:@Lazy、spring.main.lazy-initialization=true 推遲非核心 Bean 建立
  • 掃描範圍:精確配置 @ComponentScan basePackages,避免全盤掃描
  • 排除自動配置:@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
  • JVM 參數:-XX:TieredStopAtLevel=1(開發模式)、-Xshare:on(CDS 共享類別資料)

六、除錯技巧


						
						
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved