springboot启动类--SpringApplication.run()详解

本文深入剖析SpringBoot项目的启动过程,从创建SpringApplication对象到调用run方法,详细解读上下文准备、容器初始化等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

实习的第一个项目是利用springboot完成一个需求,在项目搭建的过程中真正感受到springboot的强大,springboot的起步依赖以及自动配置特性简直不要太爽,在项目搭建的过程中解放了我们的小手。而springboot的启动也非常简单,只需要启动springboot的启动类,springboot会帮助我们准备所有的环境,包括server,监听器,装配spring的上下文等等,springboot的启动类如此强大我们没有理由不去深究一下。

目录

启动类

创建SpringApplication对象

调用run方法

总结


启动类

@SpringBootApplication
public class SpringTestApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringTestApplication.class, args);
	}

}

SpringTestApplication类执行main方法,main方法调用SpringApplication的run方法。

run方法干了两件事:

  1. 创建SpringApplication对象
  2. 利用创建好的SpringApplication对象调用run方法

创建SpringApplication对象

在执行SpringApplication的run方法时首先会创建一个SpringApplication类的对象,利用构造方法创建SpringApplication对象时会调用initialize方法。

SpringApplication.run()方法的基本调用流程:

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);
	}

public SpringApplication(Object... sources) {
		initialize(sources);
	}

initialize方法:

private void initialize(Object[] sources) {
    // 在sources不为空时,保存配置类
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    // 判断是否为web应用
    this.webEnvironment = deduceWebEnvironment();
    // 获取并保存容器初始化类,通常在web应用容器初始化使用
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    // 获取并保存监听器
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 从堆栈信息获取包含main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}

调用run方法

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    // 配置属性
    configureHeadlessProperty();
    // 获取监听器
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的SpringApplicationRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动监听
    // 调用每个SpringApplicationRunListener的starting方法
    listeners.starting();
    try {
        // 将参数封装到ApplicationArguments对象中
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        // 准备环境
        // 触发监听事件——调用每个SpringApplicationRunListener的environmentPrepared方法
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
        // 从环境中取出Banner并打印
        Banner printedBanner = printBanner(environment);
        // 依据是否为web环境创建web容器或者普通的IOC容器
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        // 准备上下文
        // 1.将environment保存到容器中
        // 2.触发监听事件——调用每个SpringApplicationRunListeners的contextPrepared方法
        // 3.调用ConfigurableListableBeanFactory的registerSingleton方法向容器中注入applicationArguments与printedBanner
        // 4.触发监听事件——调用每个SpringApplicationRunListeners的contextLoaded方法
        prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
        // 刷新容器,完成组件的扫描,创建,加载等
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        // 触发监听事件——调用每个SpringApplicationRunListener的finished方法
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        // 返回容器
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}

总结

SpringApplication.run一共做了两件事

  1. 创建SpringApplication对象;在对象初始化时保存事件监听器,容器初始化类以及判断是否为web应用,保存包含main方法的主配置类。
  2. 调用run方法;准备spring的上下文,完成容器的初始化,创建,加载等。会在不同的时机触发监听器的不同事件。

参考文献:

【1】.https://www.cnblogs.com/davidwang456/p/5846513.html

【2】.https://mp.weixin.qq.com/s/O2zS2yyLy1Vu_aPtR28wiQ

<think>好的,我现在需要帮助用户理解从`SpringApplication.run(Application.class, args);`开始的Spring Boot启动流程。用户之前已经询问过Bean的创建、初始化和赋值的源码,现在他们可能想进一步了解整个应用的启动过程,特别是Spring Boot是如何启动并初始化应用的。 首先,我需要回忆一下Spring Boot的启动流程。记得`SpringApplication.run()`是入口方法,它会触发自动配置、上下文初始化、Bean加载等步骤。用户可能对底层机制感兴趣,比如如何加载配置、如何启动内嵌服务器,或者整个生命周期中的关键步骤。 接下来,我需要确定用户的背景。他们可能是一个开发者,正在学习Spring Boot,或者在工作中需要调试启动问题。因此,我需要详细但清晰地解释每个阶段,包括关键类和方法,同时避免过于晦涩的术语。 我需要组织回答的结构。可能按照启动流程的顺序,从创建SpringApplication实例开始,到准备环境、创建上下文、刷新上下文,最后启动内嵌服务器。每个步骤要说明做了什么,涉及哪些核心类,以及它们的作用。 还要注意用户之前的提问中提到了源码解析,所以这次回答可能需要引用具体的源码位置,比如`SpringApplication`类的构造方法、`run()`方法的具体实现,以及`AbstractApplicationContext`的`refresh()`方法。这能帮助用户更深入地理解内部机制。 另外,用户可能对自动配置的原理感兴趣,比如`@SpringBootApplication`背后的机制,如何加载`spring.factories`,以及条件注解如何工作。这部分需要简要提及,但不要过于深入,以免偏离启动流程的主线。 还需要考虑性能优化和常见问题,比如启动时的监听器、事件机制,或者如何自定义启动过程。但根据用户当前的问题,可能暂时不需要展开这些,除非用户后续有更多问题。 最后,我应该使用图表或流程图来辅助说明,比如Mermaid的流程图,帮助用户可视化整个流程。同时,代码示例和源码位置的引用能增加回答的可信度和实用性。 总结一下,回答的结构应该是: 1. 入口方法`run()`的作用和两个阶段。 2. 详细分解每个阶段,包括实例创建、环境准备、上下文创建和刷新。 3. 关键扩展点,如监听器、自动配置、Bean加载。 4. 图示和源码引用,帮助用户理解。 5. 可能的后续步骤或相关知识点,引导用户进一步学习。 需要确保语言简洁,逻辑清晰,逐步引导用户理解整个启动过程。同时,注意使用中文术语,避免直译导致的歧义。</think>以下是Spring Boot应用启动过程的核心源码解析(基于Spring Boot 2.5.x版本): --- ### 一、启动流程全景图 ```mermaid graph TD A[run()入口] --> B[创建SpringApplication实例] B --> C[执行run方法] C --> D[准备环境] D --> E[创建应用上下文] E --> F[刷新上下文] F --> G[执行Runners] ``` --- ### 二、启动源码深度解析 #### 1. 入口方法`SpringApplication.run()` **源码位置**:`SpringApplication.java` ```java public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); // 分两个阶段 } ``` --- ### 三、SpringApplication实例化阶段 #### 1. 构造方法核心逻辑 ```java public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 1. 设置主配置类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 2. 推断WEB应用类型(Servlet/Reactive) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 3. 加载初始化器(META-INF/spring.factories) setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 4. 加载监听器(关键事件监听) setListeners(getSpringFactoriesInstances(ApplicationListener.class)); // 5. 推断主启动类 this.mainApplicationClass = deduceMainApplicationClass(); } ``` --- ### 四、run()方法执行阶段 #### 1. 完整执行流程 **源码位置**:`SpringApplication#run` ```java public ConfigurableApplicationContext run(String... args) { // 阶段1:启动准备 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); // 获取运行监听器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); // 发送ApplicationStartingEvent try { // 阶段2:环境准备 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 阶段3:创建上下文 context = createApplicationContext(); // 阶段4:上下文预处理 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 阶段5:刷新上下文(核心!) refreshContext(context); // 阶段6:后置处理 afterRefresh(context, applicationArguments); stopWatch.stop(); // 发送ApplicationReadyEvent listeners.started(context); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } return context; } ``` --- ### 五、关键阶段详解 #### 1. 环境准备(prepareEnvironment) ```java private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 创建环境(根据WEB类型创建对应环境) ConfigurableEnvironment environment = getOrCreateEnvironment(); // 配置PropertySources和Profiles configureEnvironment(environment, applicationArguments.getSourceArgs()); // 发送EnvironmentPreparedEvent listeners.environmentPrepared(environment); // 绑定Spring Boot配置(将环境变量绑定到SpringBootApplication对象) bindToSpringApplication(environment); return environment; } ``` #### 2. 上下文创建(createApplicationContext) ```java protected ConfigurableApplicationContext createApplicationContext() { // 根据webApplicationType选择上下文类型 return this.applicationContextFactory.create(this.webApplicationType); } // 默认实现(AnnotationConfigServletWebServerApplicationContext等) static { ApplicationContextFactory DEFAULT = (webApplicationType) -> { switch (webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } }; } ``` #### 3. 上下文刷新(refreshContext) **核心源码位置**:`AbstractApplicationContext#refresh` ```java public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准备刷新(记录启动时间、初始化属性源等) prepareRefresh(); // 2. 获取新的BeanFactory(重要!) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. BeanFactory预处理(注册环境相关的Bean) prepareBeanFactory(beanFactory); // 4. BeanFactory后处理(加载配置类、组件扫描等) postProcessBeanFactory(beanFactory); // 5. 执行BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册BeanPostProcessor registerBeanPostProcessors(beanFactory); // 7. 初始化MessageSource(国际化) initMessageSource(); // 8. 初始化事件广播器 initApplicationEventMulticaster(); // 9. 子类扩展方法(如创建Web服务器) onRefresh(); // 10. 注册监听器 registerListeners(); // 11. 完成Bean初始化(重点!实例化所有非懒加载单例Bean) finishBeanFactoryInitialization(beanFactory); // 12. 完成刷新(发布ContextRefreshedEvent) finishRefresh(); } } ``` --- ### 六、自动配置原理 #### 1. @SpringBootApplication注解 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Configuration @EnableAutoConfiguration // 核心注解 @ComponentScan public @interface SpringBootApplication { // ... } ``` #### 2. 自动配置触发流程 ```mermaid sequenceDiagram participant A as @EnableAutoConfiguration participant B as AutoConfigurationImportSelector participant C as spring.factories participant D as @Conditional条件判断 A->>B: 选择导入配置 B->>C: 加载META-INF/spring.factories C->>B: 返回自动配置类列表 B->>D: 执行条件注解过滤 D->>B: 返回有效配置类 B->>Spring: 注册有效配置类 ``` --- ### 七、内嵌Web服务器启动 #### 1. Web服务器创建流程 ```java // ServletWebServerApplicationContext protected void onRefresh() { super.onRefresh(); try { createWebServer(); // 创建Web服务器 } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { // 从BeanFactory获取ServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // 创建WebServer(Tomcat/Jetty/Undertow) this.webServer = factory.getWebServer(getSelfInitializer()); } ``` --- ### 八、启动后扩展点 #### 1. 常用扩展接口 | 接口类型 | 执行时机 | 典型用途 | |---------------------------|----------------------------|--------------------------| | ApplicationRunner | 上下文刷新完成后 | 启动后执行初始化任务 | | CommandLineRunner | 同ApplicationRunner | 命令行参数处理 | | ApplicationListener | 监听特定事件 | 监控启动过程 | | BeanPostProcessor | Bean初始化前后 | Bean增强处理 | ```java // 示例:ApplicationRunner实现 @Component public class MyRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { System.out.println("应用启动完成!"); } } ``` --- ### 九、性能优化建议 1. **减少组件扫描范围**: ```properties spring.component-scan.base-packages=com.example.core ``` 2. **延迟初始化**: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.setLazyInitialization(true); // 启用全局延迟初始化 app.run(args); } } ``` 3. **排除不必要的自动配置**: ```java @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, CacheAutoConfiguration.class }) ``` --- ### 十、调试技巧 在启动时添加JVM参数: ```bash -Dlogging.level.org.springframework=DEBUG -Ddebug # 打印自动配置报告 ``` 通过这个完整的启动流程分析,可以清晰看到Spring Boot如何通过约定大于配置的原则,将复杂的Spring生态整合为简单易用的启动过程。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值