当前位置: 首页 > news >正文

Spring Boot 启动流程深度解析:从源码到实践

Spring Boot 启动流程深度解析:从源码到实践

Spring Boot 作为 Java 开发的主流框架,其 “约定大于配置” 的理念极大提升了开发效率。本文将从源码层面深入解析 Spring Boot 的启动流程,并通过代码示例展示其工作机制。

一、Spring Boot 启动的入口点

Spring Boot 应用的启动通常从一个包含main方法的类开始:

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

@SpringBootApplication是一个组合注解,包含:

  • @SpringBootConfiguration:声明这是一个配置类
  • @EnableAutoConfiguration:启用自动装配机制
  • @ComponentScan:启用组件扫描

SpringApplication.run()是启动的核心方法,下面我们深入分析其执行流程。

二、SpringApplication 的初始化

当调用SpringApplication.run()时,首先会创建SpringApplication实例:

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的构造函数会执行一系列初始化操作:

public SpringApplication(Class<?>... primarySources) {this(null, primarySources);
}@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 1. 判断应用类型(REACTIVE, SERVLET, NONE)this.webApplicationType = WebApplicationType.deduceFromClasspath();// 2. 设置ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 3. 设置ApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 4. 推断主应用类(包含main方法的类)this.mainApplicationClass = deduceMainApplicationClass();
}

关键步骤解析:

  1. 应用类型推断:根据类路径中的类推断应用类型(WebFlux、Servlet 或普通应用)
  2. 初始化器加载:从META-INF/spring.factories加载ApplicationContextInitializer
  3. 监听器加载:从META-INF/spring.factories加载ApplicationListener
  4. 主应用类推断:通过栈轨迹找到包含 main 方法的类

三、SpringApplication.run () 方法解析

run()方法是 Spring Boot 启动的核心逻辑:

public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();// 1. 获取并启动所有SpringApplicationRunListenerSpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 2. 准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);// 3. 打印BannerBanner printedBanner = printBanner(environment);// 4. 创建ApplicationContextcontext = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// 5. 准备上下文prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 6. 刷新上下文refreshContext(context);// 7. 刷新后的处理afterRefresh(context, applicationArguments);// 8. 计时结束Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}// 9. 发布应用启动完成事件listeners.started(context, timeTakenToStartup);// 10. 调用所有RunnerscallRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 11. 发布应用就绪事件Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {if (context != null) {context.close();throw new IllegalStateException("Error handling failed", ex);}throw new IllegalStateException(ex);}return context;
}

四、关键步骤详解

1. 启动监听器(SpringApplicationRunListeners)

Spring Boot 通过事件机制在启动的不同阶段发布事件,允许开发者介入:

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

核心监听器包括EventPublishingRunListener,它会发布一系列事件:

  • ApplicationStartingEvent
  • ApplicationEnvironmentPreparedEvent
  • ApplicationContextInitializedEvent
  • ApplicationPreparedEvent
  • ApplicationStartedEvent
  • ApplicationReadyEvent
2. 环境准备(prepareEnvironment)

环境准备包括创建并配置ConfigurableEnvironment,加载属性源和配置文件:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext,ApplicationArguments applicationArguments) {// 创建环境(Web或非Web)ConfigurableEnvironment environment = getOrCreateEnvironment();// 配置环境configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);// 发布环境准备事件listeners.environmentPrepared(bootstrapContext, environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;
}
3. 创建应用上下文(createApplicationContext)

根据应用类型创建不同的ApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {// 根据应用类型选择上下文类switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable to create a default ApplicationContext, please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
4. 准备应用上下文(prepareContext)

对创建好的上下文进行初始化,包括设置环境、加载源、应用初始化器等:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 设置环境context.setEnvironment(environment);// 应用上下文后置处理器postProcessApplicationContext(context);// 应用初始化器applyInitializers(context);// 发布上下文初始化事件listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// 添加资源加载器和主应用类ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// 设置懒加载if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// 加载源(主配置类)Set<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));// 发布上下文加载完成事件listeners.contextLoaded(context);
}
5. 刷新应用上下文(refreshContext)

调用AbstractApplicationContext.refresh()方法,这是 Spring 框架的核心逻辑,包括:

  • BeanFactory 的创建与初始化
  • BeanDefinition 的加载
  • Bean 的创建与依赖注入
  • 自动装配的处理
  • 事件发布器的初始化
  • 嵌入式 Servlet 容器的启动(如果是 Web 应用)
private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}refresh(context);
}protected void refresh(ConfigurableApplicationContext context) {context.refresh();
}
6. 调用应用 Runner(callRunners)

启动完成后,调用所有实现了ApplicationRunnerCommandLineRunner的 Bean:

private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();// 添加所有ApplicationRunnerrunners.addAll(context.getBeansOfType(ApplicationRunner.class).values());// 添加所有CommandLineRunnerrunners.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);}}
}

五、自定义启动流程

开发者可以通过以下方式自定义启动流程:

1. 添加 ApplicationContextInitializer
public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {// 在上下文刷新前执行自定义逻辑System.out.println("Custom initializer called");}
}

注册方式:

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication app = new SpringApplication(MyApplication.class);app.addInitializers(new CustomApplicationContextInitializer());app.run(args);}
}
2. 添加 ApplicationListener
public class CustomApplicationListener implements ApplicationListener<ApplicationStartedEvent> {@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {// 应用启动后执行System.out.println("Application started!");}
}

注册方式:

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication app = new SpringApplication(MyApplication.class);app.addListeners(new CustomApplicationListener());app.run(args);}
}
3. 实现 CommandLineRunner 或 ApplicationRunner
@Component
public class CustomCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("CommandLineRunner executed with args: " + Arrays.toString(args));}
}

六、启动流程总结

Spring Boot 的启动流程可以概括为以下关键步骤:

  1. 初始化 SpringApplication
    • 推断应用类型
    • 加载初始化器和监听器
    • 确定主应用类
  2. 执行 run () 方法
    • 发布启动事件
    • 准备环境(加载配置属性)
    • 创建应用上下文
    • 准备上下文(设置环境、加载源)
    • 刷新上下文(核心 Spring 容器初始化)
    • 调用应用 Runner
    • 发布就绪事件
  3. 核心机制
    • 事件机制:通过ApplicationEventApplicationListener实现
    • 自动装配:在上下文刷新阶段通过@EnableAutoConfiguration触发
    • 条件注解:控制配置类的加载条件

理解 Spring Boot 的启动流程有助于开发者更好地调试应用、自定义启动行为,以及解决启动过程中遇到的问题。通过扩展点(Initializers、Listeners、Runners),开发者可以在不修改核心代码的情况下介入启动流程,实现各种定制需求。

http://www.xdnf.cn/news/691435.html

相关文章:

  • 【烧脑算法】定长滑动窗口:算法题中的“窗口”智慧
  • MySQL OCP 与 Oracle OCP 认证,怎么选?
  • 怎样将win11+ubuntu双系统的ubuntu从机械硬盘迁移至固态硬盘(1)
  • 【Elasticsearch】track_total_hits
  • CAD图纸中的文字看不到,这是什么原因?
  • 传输线的特性阻抗和传播延迟
  • DL00916-基于深度学习的金枪鱼各类别目标检测含完整数据集
  • 131.在 Vue3 中使用 OpenLayers 测量长度和面积,并支持 modifyend 动态更新数值
  • 不打架的协议互通,modbus转profibus网关的总线的高效互通方案
  • C53-字符串引入和注意事项.
  • JavaScript 中 this 指向全解析:从基础到 Vue 应用
  • 【加密算法】
  • Qt/C++开发监控GB28181系统/sip协议/同时支持udp和tcp模式/底层协议解析
  • 【Microsoft 365可用】PPT一键取消所有超链接
  • Python 进阶【二】:多进程编程
  • TCP三次握手/四次握手-TCP/IP四层模型-SSL/TLS-HTTP-HTTPS
  • Bootstrap法进行随机模拟
  • 第11章:工程组织与系列总结
  • 8086 处理器寄存器超详细解析:从原理到实战
  • 三分钟打通Stable Diffusion提示词(附实战手册)
  • TDengine 运维——用户和权限
  • Agent 的7 中设计模式
  • 4.Consul服务注册与发现
  • 【深度学习】10. 深度推理(含链式法则详解)RNN, LSTM, GRU,VQA
  • 33.第二阶段x64游戏实战-InLineHook
  • Jmeter——JDBC连接数据库相关
  • 【vscode】切换英文字母大小写快捷键如何配置
  • 《Google I/O 2025:AI浪潮下的科技革新风暴》
  • 宁夏农业科技:创新引领,赋能现代农业新篇章
  • c语言实现Linux命令行补全机制