当前位置: 首页 > 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),开发者可以在不修改核心代码的情况下介入启动流程,实现各种定制需求。

相关文章:

  • 高温炉制造企业Odoo ERP实施规划与深度分析报告
  • 免杀二 内存函数与加密
  • 影响沉金价格的因素如何体现在多层电路板制造上?
  • 智警杯备赛--数据库管理与优化
  • 基于stm32风速风向温湿度和瓦斯检测(仿真+代码)
  • 2025.05.28【读书笔记】|如何用SILVA和RFAM数据库高效去除rRNA污染
  • C++11:系统类型增强
  • Redis keydb dragonfly skytable
  • uni-app开发特殊社交APP
  • 人工智能在智慧物流中的创新应用与未来趋势
  • Flask集成pyotp生成动态口令
  • 时序数据库IoTDB如何快速高效地存储时序数据
  • 深兰科技陈海波率队考察南京,加速AI医诊大模型区域落地应用
  • Android 缓存应用冻结器(Cached Apps Freezer)
  • 深兰科技董事长陈海波率队考察南京,加速AI大模型区域落地应用
  • 宁夏农业科技:创新引领,赋能现代农业新篇章
  • C++23:std::print和std::println格式化输出新体验
  • AWS 创建VPC 并且添加权限控制
  • PR2020+MS1824+MS7210+MS2130 1080P@60Hz USB3.0采集
  • 数据结构之堆(topk问题、堆排序)
  • 网站建设专业特长/如何自己做网页
  • 阿里巴巴怎么做自己的免费网站/淘宝指数查询入口
  • 网站建设哪个比较好/全国新闻媒体发稿平台
  • phpmysql网站开发案例/营销推广策划及渠道
  • lnmp搭建后怎么做网站/百度关键词优化多少钱
  • 哪家做网站好 成都/信息流广告的特点