前言
在上一篇中,我们已经成功搭建了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启动就像造一辆汽车:
- 设计阶段(SpringApplication构造器)
- 拿到设计图纸(
primarySources) - 决定造什么车(
WebApplicationType) - 招聘各种工人(各种
Initializer和Listener)
- 拿到设计图纸(
- 生产线准备(run()方法开始)
- 地基工人进场(
BootstrapRegistryInitializer) - 车间主任检查设备(
ApplicationContextInitializer) - 启动监控系统(
ApplicationListener)
- 地基工人进场(
- 正式生产(refresh()方法)
- 零件生产线运转(Bean创建)
- 组装整车(Bean依赖注入)
- 质量检测(Bean后置处理)
- 出厂上路(启动完成)
- 汽车可以开了(应用可以接收请求)
- 监控系统持续工作(事件监听)
🎯 给新手的核心理解要点:
为什么要有这么多"工人"?
- 分工明确:每个工人只负责自己的专业领域
- 可扩展性:你可以自己招聘新工人(自定义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=ALWAYS7.2 关键断点设置建议
设置断点观察启动流程:
SpringApplication构造器SpringApplication.run()方法入口prepareEnvironment()方法createApplicationContext()方法refreshContext()方法onRefresh()方法(Web服务器启动)
7.3 启动日志分析
启用debug日志观察启动细节:
# application.properties logging.level.org.springframework.boot=DEBUG logging.level.org.springframework.context=DEBUG8. 启动过程总结与核心流程图
8.1 启动过程核心步骤
8.2 关键设计模式应用
- 模板方法模式:
AbstractApplicationContext.refresh()定义了算法骨架 - 观察者模式:Spring事件机制
- 工厂模式:
ApplicationContext创建 - 策略模式:不同的Web应用类型处理
- 责任链模式:
BeanPostProcessor调用链
结语
通过本文的深入分析,我们揭示了Spring Boot启动过程的完整流程。从SpringApplication初始化到Web服务器启动,每一个步骤都体现了Spring Boot"约定大于配置"的设计哲学。
关键收获:
- Spring Boot启动是Spring Framework生命周期与Boot特有扩展的结合
- 自动配置通过
SpringFactoriesLoader机制实现 - Web服务器启动在
onRefresh阶段触发 - 事件机制为启动过程提供了丰富的扩展点
下篇预告:在下一篇文章中,我们将深入Spring Boot的自动配置机制,解析@Conditional注解体系,并学习如何编写自定义的Starter。
思考题:
- 如果在启动过程中需要读取配置文件中的自定义配置,应该在哪个阶段进行?
- 如何在不修改源码的情况下,在Bean创建前后插入自定义逻辑?
- Spring Boot如何支持多种内嵌Web服务器(Tomcat、Jetty、Undertow)的自动切换?
欢迎在评论区分享你的理解和遇到的问题!