Spring Boot 启动原理详解(基于 2.2.2.RELEASE):从入口到应用就绪的全流程 Spring Boot 的启动过程本质是 “初始化环境→创建上下文→加载 Bean→触发扩展点 ” 的有序流程,核心入口是 main 方法,通过 SpringApplication 类封装了所有启动逻辑。从 “启动入口→SpringApplication 实例化→run 方法执行→上下文准备与刷新 ” 四个核心阶段,拆解 Spring Boot 2.2.2.RELEASE 版本的启动原理,帮你理解每个步骤的核心作用。
启动入口:main 方法的 “一键触发” Spring Boot 应用的启动入口是标注 @SpringBootApplication 的主类中的 main 方法,这行代码是所有启动逻辑的起点:
1 2 3 4 5 6 7 @SpringBootApplication public class ConsulApp { public static void main (String[] args) { SpringApplication.run(ConsulApp.class, args); } }
静态 run 方法的底层逻辑 进入 SpringApplication.run(Class<?> primarySource, String[] args) 源码,发现它做了两件事:
实例化 SpringApplication :初始化启动所需的核心组件(如监听器、初始化器);
调用实例的 run 方法 :执行实际的启动流程(环境准备、上下文创建、Bean 加载等)。
1 2 3 4 5 public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return new SpringApplication (primarySources).run(args); }
阶段一:SpringApplication 实例化 —— 启动前的 “准备工作” SpringApplication 的构造方法负责初始化启动所需的核心配置,为后续 run 方法执行打基础。构造方法主要完成 6 件事:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet <>(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); }
关键步骤详解 1. 判断 Web 应用类型(WebApplicationType) WebApplicationType 决定 Spring Boot 后续创建哪种 ApplicationContext(上下文),通过类路径中是否存在特定类来判断:
WebApplicationType.NONE :非 Web 应用(类路径中无 javax.servlet.Servlet 和 org.springframework.web.reactive.DispatcherHandler);
WebApplicationType.SERVLET :传统 Servlet Web 应用(存在 javax.servlet.Servlet 和 org.springframework.web.context.ConfigurableWebApplicationContext);
WebApplicationType.REACTIVE :响应式 Web 应用(存在 org.springframework.web.reactive.DispatcherHandler)。
源码依据(WebApplicationType.deduceFromClasspath()):
1 2 3 4 5 6 7 8 9 10 11 12 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; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null )) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
2. 加载初始化器与监听器(SpringFactoriesLoader) getSpringFactoriesInstances 方法是 Spring Boot 的 “核心加载器”,通过 SpringFactoriesLoader 从类路径下的 META-INF/spring.factories 文件中加载指定接口的实现类:
ApplicationContextInitializer :上下文初始化器,用于在上下文刷新前修改配置(如动态添加属性);
ApplicationListener :应用监听器,用于监听启动过程中的事件(如 ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent)。
示例:spring.factories 中配置的初始化器和监听器:
1 2 3 4 5 6 7 8 9 org.springframework.context.ApplicationContextInitializer =\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer org.springframework.context.ApplicationListener =\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.embedded.EmbeddedServletContainerListener
3. 推断主启动类(deduceMainApplicationClass) 通过 “构造运行时异常→遍历异常栈” 的巧妙方式,找到包含 main 方法的类(即我们的 ConsulApp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException ().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main" .equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { } return null ; }
阶段二:执行 run 方法 —— 启动的 “核心流程” SpringApplication 实例化后,调用 run(String... args) 方法,这是 Spring Boot 启动的核心逻辑。run 方法可拆解为 11 个关键步骤,覆盖 “启动前→启动中→启动后” 全链路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch (); stopWatch.start(); ConfigurableApplicationContext context = null ; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList <>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments (args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class [] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { new StartupInfoLogger (this .mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException (ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null ); throw new IllegalStateException (ex); } return context; }
关键步骤详解(核心是 NO4、NO7、NO8、NO10) 1. NO4:准备环境(prepareEnvironment) prepareEnvironment 是启动的 “配置加载阶段”,负责加载应用的配置(配置文件、系统变量、命令行参数)并激活环境,核心操作:
初始化 ConfigurableEnvironment(根据 Web 类型创建 StandardServletEnvironment 或 StandardEnvironment);
加载配置源(application.yml/application.properties、命令行参数 --server.port=8081 等);
激活环境(根据 spring.profiles.active 激活对应环境,如 dev/prod);
广播 ApplicationEnvironmentPreparedEvent 事件(通知监听器 “环境已准备完成”)。
源码片段(关键逻辑):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); return environment; }
2. NO7:准备上下文(prepareContext) prepareContext 是 “上下文初始化阶段”,在上下文刷新前完成配置,核心操作:
将环境绑定到上下文(context.setEnvironment(environment));
执行所有 ApplicationContextInitializer 的 initialize 方法(修改上下文配置);
广播 ApplicationContextInitializedEvent 事件(通知 “上下文已初始化”);
注册特殊 Bean(如 springApplicationArguments、springBootBanner);
加载主启动类中的 Bean 定义(扫描 @Component/@Bean 等注解);
广播 ApplicationPreparedEvent 事件(通知 “上下文已准备完成”)。
源码片段(关键逻辑):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } load(context, sources.toArray(new Object [0 ])); listeners.contextLoaded(context); }
3. NO8:刷新上下文(refreshContext) refreshContext 是 Spring Boot 启动的 “核心中的核心”,本质是调用 Spring 原生的 AbstractApplicationContext#refresh() 方法 —— 这是 Spring 容器初始化的标准流程,负责:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public void refresh () throws BeansException, IllegalStateException { synchronized (this .startupShutdownMonitor) { prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory); invokeBeanFactoryPostProcessors(beanFactory); registerBeanPostProcessors(beanFactory); initMessageSource(); initApplicationEventMulticaster(); onRefresh(); registerListeners(); finishBeanFactoryInitialization(beanFactory); finishRefresh(); } catch (BeansException ex) { destroyBeans(); cancelRefresh(ex); throw ex; } finally { resetCommonCaches(); } } }
4. NO10:执行 Runner(callRunners) callRunners 是启动的 “最后扩展点”,负责执行所有 ApplicationRunner 和 CommandLineRunner 实现类的 run 方法 —— 这两个接口用于在应用启动后执行初始化逻辑(如加载缓存、初始化数据)。
核心逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private void callRunners (ApplicationContext context, ApplicationArguments applicationArguments) { List<Object> runners = new ArrayList <>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet <>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, applicationArguments); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, applicationArguments); } } }
阶段三:启动完成与事件广播 run 方法执行到最后,会通过 SpringApplicationRunListeners 广播两个关键事件:
started 事件 :上下文刷新完成后触发,表示 “应用已启动但 Runner 未执行”;
running 事件 :Runner 执行完成后触发,表示 “应用已完全就绪,可对外提供服务”。
若启动过程中抛出异常,会触发 failed 事件,并通过 SpringBootExceptionReporter 生成异常报告,帮助定位问题(如配置错误、依赖缺失)。
总结:Spring Boot 启动的核心链路 结合上述分析,Spring Boot 2.2.2.RELEASE 的启动流程可概括为 “3 大阶段 + 11 个关键步骤 ”,用一句话总结:
从 main 方法入口,先实例化 SpringApplication 完成启动前准备(Web 类型判断、初始化器 / 监听器加载),再执行 run 方法加载环境、创建上下文、刷新容器、执行 Runner,最终广播 “应用就绪” 事件,完成启动。
用流程图更直观展示:
1 2 3 4 5 6 7 8 9 10 11 12 13 main 方法 → SpringApplication 实例化(6步)→ run 方法执行(11步)→ 应用就绪 ↓ ↓ 1. 资源加载器设置 1. 启动计时 2. 主启动类去重 2. 上下文/异常报告器初始化 3. Web 类型判断 3. 监听器加载与 starting 事件 4. 初始化器加载 4. 环境准备(配置+激活) 5. 监听器加载 5. Banner 打印 6. 主启动类推断 6. 上下文创建 7. 上下文准备(初始化器+Bean加载) 8. 上下文刷新(Spring 核心流程) 9. 计时停止与 started 事件 10. Runner 执行 11. running 事件广播
理解这一流程,能帮你快速定位启动问题(如环境配置错误、Runner 执行失败),也为自定义启动逻辑(如添加初始化器、监听器)提供基础,是深入 Spring Boot 的关键