news 2026/4/19 23:06:49

深入Spring Boot源码(二):启动过程深度剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入Spring Boot源码(二):启动过程深度剖析

前言

在上一篇中,我们已经成功搭建了Spring Boot源码研究环境。

现在,让我们深入Spring Boot的核心——启动过程。

当你运行一个Spring Boot应用的main方法时,背后究竟发生了什么?

本文将带你从SpringApplication.run()开始,一步步揭开Spring Boot启动的神秘面纱。

在正式开始介绍之前,先用形象的方式类比SpringApplication这个类用途。

0、把SpringApplication想象成"汽车制造工厂"

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

这就像在说:"我们要建造一个汽车制造工厂,这是我们的原料供应商(resourceLoader)和核心设计图纸(primarySources)"


🔧 一步步拆解这个"汽车工厂"的建造过程:

1.接收核心设计图纸

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

通俗解释

工厂收到你给的核心设计图纸(就是你的@SpringBootApplication主类)。比如你传入了DemoApplication.class,工厂就知道:"哦,我要按照这个设计来造车!"

2.判断要造什么类型的车

this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());

通俗解释

工厂自动检查你的"零件仓库"(类路径),判断你要造什么车:

  • 发现有Servlet零件 → 造传统Web汽车(SERVLET)
  • 发现有WebFlux零件 → 造新能源响应式汽车(REACTIVE)
  • 什么Web零件都没有 → 造普通家用轿车(NONE)

3.招聘第一批基础工人

this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

通俗解释

工厂先招聘地基施工队(BootstrapRegistryInitializer),这些工人在工厂刚建好时就进场,负责打地基、铺管线等最基础的工作。

4.招聘车间主任和质检员

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

通俗解释

接着招聘车间主任(ApplicationContextInitializer),他们负责在各个车间(ApplicationContext)正式开工前,检查设备、调整生产线布局。

5.安装监控摄像头和警报器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

通俗解释

安装监控系统(ApplicationListener),这些"耳朵"会监听工厂里发生的各种事件:机器启动、零件到位、故障报警等。

6.确定总设计师办公室

this.mainApplicationClass = deduceMainApplicationClass();

通俗解释

自动找到总设计师的办公室(主应用类),这样工厂有任何问题都知道该找谁汇报。


🚗 完整造车流程比喻:

想象一下整个Spring Boot启动就像造一辆汽车

  1. 设计阶段(SpringApplication构造器)
    • 拿到设计图纸(primarySources
    • 决定造什么车(WebApplicationType
    • 招聘各种工人(各种InitializerListener
  1. 生产线准备(run()方法开始)
    • 地基工人进场(BootstrapRegistryInitializer
    • 车间主任检查设备(ApplicationContextInitializer
    • 启动监控系统(ApplicationListener
  1. 正式生产(refresh()方法)
    • 零件生产线运转(Bean创建)
    • 组装整车(Bean依赖注入)
    • 质量检测(Bean后置处理)
  1. 出厂上路(启动完成)
    • 汽车可以开了(应用可以接收请求)
    • 监控系统持续工作(事件监听)

🎯 给新手的核心理解要点:

为什么要有这么多"工人"?

  • 分工明确:每个工人只负责自己的专业领域
  • 可扩展性:你可以自己招聘新工人(自定义Initializer/Listener)
  • 生命周期管理:工人们按照固定顺序工作,不会乱套

这个构造器的作用总结:

// 用一句话总结这个构造器: "收集所有必要的工具和工人,为后续的汽车制造做好准备"

下一步会发生什么:

构造器只是准备工作,真正的"造车"是在run()方法中:

// 准备工作完成,开始造车! SpringApplication.run(DemoApplication.class, args);

💡 形象记忆技巧:

把SpringApplication想象成:

  • resourceLoader= 原料供应商
  • primarySources= 核心设计图纸
  • BootstrapRegistryInitializer= 地基施工队
  • ApplicationContextInitializer= 车间主任
  • ApplicationListener= 监控摄像头
  • mainApplicationClass= 总设计师办公室

这样下次看到这段代码,你就能立即在脑海中构建出这个"汽车工厂"的生动画面了!

理解了这个比喻,后续学习run()方法时就会轻松很多,因为那只是按部就班地执行这个"造车流程"而已。

1. SpringApplication初始化:启动的起点

1.1 入口点分析

让我们从最简单的Spring Boot应用开始:

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

关键源码位置:spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

接下来用一个非常形象的比喻来理解SpringApplication的作用和执行流程。

1.2 构造器初始化过程

// SpringApplication构造器核心逻辑 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 关键步骤1:推断Web应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 关键步骤2:加载应用上下文初始化器 setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 关键步骤3:加载应用事件监听器 setListeners(getSpringFactoriesInstances(ApplicationListener.class)); // 关键步骤4:推断主应用类 this.mainApplicationClass = deduceMainApplicationClass(); }

1.3 Web应用类型推断

// WebApplicationType.deduceFromClasspath() 源码分析 static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; // 响应式Web应用 } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; // 非Web应用 } } return WebApplicationType.SERVLET; // Servlet Web应用 }

调试技巧:deduceFromClasspath()方法设置断点,观察类路径检测逻辑。

2. run方法执行流程:启动的核心

2.1 run方法整体架构

public ConfigurableApplicationContext run(String... args) { // 1. 启动计时器 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 2. 创建引导上下文和应用上下文 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 3. 获取SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); // 4. 发布ApplicationStartingEvent事件 listeners.starting(); try { // 5. 准备环境 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 6. 打印Banner Banner printedBanner = printBanner(environment); // 7. 创建应用上下文 context = createApplicationContext(); // 8. 准备应用上下文 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 9. 刷新上下文(Spring核心流程) refreshContext(context); // 10. 后置处理 afterRefresh(context, applicationArguments); // 11. 停止计时器 stopWatch.stop(); // 12. 发布启动完成事件 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); // 13. 执行Runner callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } listeners.running(context); return context; }

2.2 环境准备阶段

private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 创建和配置环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); // 发布EnvironmentPreparedEvent事件 listeners.environmentPrepared(environment); // 绑定环境到SpringApplication bindToSpringApplication(environment); // 转换环境(如果需要) if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } // 配置PropertySources ConfigurationPropertySources.attach(environment); return environment; }

2.3 应用上下文创建

protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { // 根据Web应用类型选择不同的上下文实现 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 create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }

3. 上下文刷新:Spring核心流程

3.1 refreshContext核心流程

// 这是Spring框架的核心方法,Spring Boot进行了封装 private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // 不在允许的环境中 } } } // 实际调用Spring的refresh方法 protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }

3.2 Spring Framework的refresh方法

// 在AbstractApplicationContext中定义,包含12个关键步骤 public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准备刷新上下文 prepareRefresh(); // 2. 获取新的BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. 准备BeanFactory prepareBeanFactory(beanFactory); try { // 4. 后置处理BeanFactory postProcessBeanFactory(beanFactory); // 5. 调用BeanFactoryPostProcessors invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册BeanPostProcessors registerBeanPostProcessors(beanFactory); // 7. 初始化MessageSource initMessageSource(); // 8. 初始化事件广播器 initApplicationEventMulticaster(); // 9. 初始化特殊Bean(由子类实现) onRefresh(); // 10. 注册监听器 registerListeners(); // 11. 完成BeanFactory初始化,实例化所有单例Bean finishBeanFactoryInitialization(beanFactory); // 12. 完成刷新过程,发布ContextRefreshedEvent事件 finishRefresh(); } catch (BeansException ex) { // 清理资源 destroyBeans(); cancelRefresh(ex); throw ex; } finally { resetCommonCaches(); } } }

3.3 Spring Boot的onRefresh扩展

// 在SpringApplication中,onRefresh方法被重写以支持Web服务器启动 protected void onRefresh(ApplicationContext context) { // 调用父类方法 super.onRefresh(context); // Spring Boot特有的:创建Web服务器 if (context instanceof ConfigurableWebServerApplicationContext) { ((ConfigurableWebServerApplicationContext) context) .setServerNamespace(this.serverNamespace); } }

4. 自动配置机制:Spring Boot的魔法核心

4.1 @SpringBootApplication注解解析

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { // 省略属性定义 }

4.2 @EnableAutoConfiguration核心机制

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }

4.3 AutoConfigurationImportSelector工作流程

// 这是自动配置的核心类 public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 1. 获取自动配置条目 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { // 2. 检查自动配置是否启用 if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 3. 获取注解属性 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 4. 获取候选配置 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 5. 移除重复配置 configurations = removeDuplicates(configurations); // 6. 根据exclude属性排除配置 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 7. 应用过滤条件 configurations = getConfigurationClassFilter().filter(configurations); // 8. 触发自动配置导入事件 fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 从META-INF/spring.factories加载自动配置类 List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. " + "If you are using a custom packaging, make sure that file is correct."); return configurations; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } }

5. Web服务器启动:内嵌容器的奥秘

5.1 Servlet Web服务器启动

// 在ServletWebServerApplicationContext中 @Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); // 创建Web服务器 } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { // 从BeanFactory获取WebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // 创建WebServer this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }

5.2 Tomcat服务器创建过程

// 在TomcatServletWebServerFactory中 @Override public WebServer getWebServer(ServletContextInitializer... initializers) { // 1. 创建Tomcat实例 Tomcat tomcat = new Tomcat(); // 2. 配置基础目录 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); // 3. 创建Connector Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); // 4. 创建Engine和Host tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); // 5. 准备Context prepareContext(tomcat.getHost(), initializers); // 6. 返回自定义的TomcatWebServer return getTomcatWebServer(tomcat); }

6. 启动扩展点:自定义启动行为

6.1 ApplicationRunner vs CommandLineRunner

// ApplicationRunner示例 @Component @Order(1) // 指定执行顺序 public class DemoApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner执行,参数: " + Arrays.toString(args.getSourceArgs())); // 应用启动后需要执行的逻辑 } } // CommandLineRunner示例 @Component @Order(2) public class DemoCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner执行,参数: " + Arrays.toString(args)); // 应用启动后需要执行的逻辑 } }

6.2 应用事件监听机制

// 监听应用启动事件 @Component public class ApplicationStartupListener implements ApplicationListener<ApplicationStartedEvent> { private static final Logger logger = LoggerFactory.getLogger(ApplicationStartupListener.class); @Override public void onApplicationEvent(ApplicationStartedEvent event) { logger.info("应用启动完成,开始执行初始化任务..."); // 执行自定义初始化逻辑 performInitialization(); } private void performInitialization() { // 初始化缓存、连接池等 } }

7. 调试与实验:验证启动过程

7.1 创建调试配置

在IDEA中创建调试配置,添加以下VM参数:

-Ddebug=true -Dspring.output.ansi.enabled=ALWAYS

7.2 关键断点设置建议

设置断点观察启动流程:

  1. SpringApplication构造器
  2. SpringApplication.run()方法入口
  3. prepareEnvironment()方法
  4. createApplicationContext()方法
  5. refreshContext()方法
  6. onRefresh()方法(Web服务器启动)

7.3 启动日志分析

启用debug日志观察启动细节:

# application.properties logging.level.org.springframework.boot=DEBUG logging.level.org.springframework.context=DEBUG

8. 启动过程总结与核心流程图

8.1 启动过程核心步骤

8.2 关键设计模式应用

  1. 模板方法模式AbstractApplicationContext.refresh()定义了算法骨架
  2. 观察者模式:Spring事件机制
  3. 工厂模式ApplicationContext创建
  4. 策略模式:不同的Web应用类型处理
  5. 责任链模式BeanPostProcessor调用链

结语

通过本文的深入分析,我们揭示了Spring Boot启动过程的完整流程。从SpringApplication初始化到Web服务器启动,每一个步骤都体现了Spring Boot"约定大于配置"的设计哲学。

关键收获:

  • Spring Boot启动是Spring Framework生命周期与Boot特有扩展的结合
  • 自动配置通过SpringFactoriesLoader机制实现
  • Web服务器启动在onRefresh阶段触发
  • 事件机制为启动过程提供了丰富的扩展点

下篇预告:在下一篇文章中,我们将深入Spring Boot的自动配置机制,解析@Conditional注解体系,并学习如何编写自定义的Starter。


思考题:

  1. 如果在启动过程中需要读取配置文件中的自定义配置,应该在哪个阶段进行?
  2. 如何在不修改源码的情况下,在Bean创建前后插入自定义逻辑?
  3. Spring Boot如何支持多种内嵌Web服务器(Tomcat、Jetty、Undertow)的自动切换?

欢迎在评论区分享你的理解和遇到的问题!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/19 2:48:40

MPV播放器软件兼容性问题解决方案与修复指南

MPV播放器软件兼容性问题解决方案与修复指南 【免费下载链接】mpv &#x1f3a5; Command line video player 项目地址: https://gitcode.com/GitHub_Trending/mp/mpv 软件兼容性问题在跨平台应用开发中尤为常见&#xff0c;本文针对MPV播放器在不同操作系统环境下遇到的…

作者头像 李华
网站建设 2026/4/17 12:13:35

【开题答辩全过程】以 个性化新闻推荐系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/4/16 14:18:16

爱尔兰耕地与农业发展解析

爱尔兰以草地畜牧业为核心&#xff0c;农业用地约450万公顷&#xff08;占国土65%&#xff09;&#xff0c;其中80%为牧场&#xff0c;形成“草-畜-出口”的高效体系&#xff0c;是欧盟重要的牛肉与乳制品供应国&#xff0c;农业GDP占比约4.3%&#xff0c;食品出口约占商品出口…

作者头像 李华
网站建设 2026/4/17 7:39:39

如何高效抓取淘宝直播弹幕数据:完整实战指南

如何高效抓取淘宝直播弹幕数据&#xff1a;完整实战指南 【免费下载链接】taobao-live-crawler A crawler on taobao live barrages. 项目地址: https://gitcode.com/gh_mirrors/ta/taobao-live-crawler 想要了解淘宝直播间里观众的真实想法吗&#xff1f;想通过弹幕数据…

作者头像 李华
网站建设 2026/4/16 23:43:57

Python测试终极指南:为什么pytest成为开发者的首选工具

Python测试终极指南&#xff1a;为什么pytest成为开发者的首选工具 【免费下载链接】pytest The pytest framework makes it easy to write small tests, yet scales to support complex functional testing 项目地址: https://gitcode.com/gh_mirrors/py/pytest 在Pyth…

作者头像 李华