1. Spring Boot启动流程全景概览
第一次接触Spring Boot时,最让我惊讶的就是它极简的启动方式。传统Spring项目需要配置一堆XML文件,而Spring Boot只需要一个标注了@SpringBootApplication的主类和一行SpringApplication.run()代码就能启动完整的Web服务。这背后究竟发生了什么?让我们从宏观视角看看整个启动流程。
Spring Boot的启动过程可以划分为三个阶段:
- 初始化阶段:创建SpringApplication实例,加载各类初始化器和监听器
- 运行时阶段:准备环境变量,创建应用上下文,执行自动配置
- Web服务阶段:内嵌Web容器(如Tomcat)的初始化和启动
我画了个简化的流程图帮助理解:
[主类启动] ↓ [SpringApplication实例化] ↓ [运行环境准备] → [监听器通知] → [Banner打印] ↓ [创建应用上下文] → [准备上下文] → [执行自动配置] ↓ [刷新上下文] → [初始化WebServer] ↓ [启动完成]2. 解密@SpringBootApplication注解
2.1 复合注解的本质
当我们创建一个标准的Spring Boot应用时,主类上必定会有这个注解:
@SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } }这个注解实际上是个"套娃",它由三个核心注解组合而成:
@SpringBootConfiguration:标识这是一个配置类@EnableAutoConfiguration:启用自动配置机制@ComponentScan:开启组件扫描
这就解释了为什么我们不加任何配置就能直接使用各种功能。比如当你引入spring-boot-starter-web依赖后,Spring Boot会自动配置Tomcat和Spring MVC。
2.2 自动配置的魔法
最神奇的@EnableAutoConfiguration注解内部是这样工作的:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { // ... }关键点在于AutoConfigurationImportSelector,它会从所有jar包的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中加载自动配置类。比如Web相关的自动配置就定义在spring-boot-autoconfigure包的对应文件中。
3. SpringApplication初始化过程
3.1 实例化阶段
当我们调用SpringApplication.run()时,首先会创建SpringApplication实例:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断应用类型(Servlet/Reactive/None) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 加载初始化器和监听器 setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners(getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }这里有个设计精妙之处:通过spring.factories机制加载扩展组件。这种SPI(服务发现)模式让Spring Boot具有极好的扩展性。
3.2 运行阶段核心逻辑
run()方法是整个启动过程的核心:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 创建应用上下文 ConfigurableApplicationContext context = createApplicationContext(); // 准备上下文 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新上下文(核心!) refreshContext(context); // 执行Runner callRunners(context, applicationArguments); stopWatch.stop(); return context; }我曾在一个性能敏感项目中,通过自定义ApplicationRunner在这个阶段预加载缓存,使首请求响应时间降低了70%。
4. 自动配置的实现原理
4.1 条件装配的智慧
Spring Boot自动配置的核心是@Conditional系列注解,比如:
@Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public class EmbeddedTomcatAutoConfiguration { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); } }这个配置类只有在classpath下存在Tomcat相关类时才会生效。这种条件判断机制确保了:
- 按需加载:只加载当前环境需要的配置
- 可覆盖性:用户自定义的Bean优先
4.2 自动配置执行流程
自动配置发生在refreshContext()阶段:
- 加载所有
AutoConfiguration.imports中定义的配置类 - 过滤掉不满足
@Conditional条件的配置 - 将剩余的配置类注册到容器中
我遇到过的一个典型问题:当同时引入Redis和Lettuce依赖时,自动配置会优先选择Lettuce连接池。要切换为Jedis,需要显式排除Lettuce的自动配置类。
5. WebServer启动过程
5.1 内嵌容器的初始化
Spring Boot 2.x之后,WebServer的启动流程变得更加清晰:
// 在AbstractApplicationContext.refresh()中 protected void onRefresh() { initWebServer(); // 初始化Web服务器 } private void initWebServer() { WebServer webServer = this.webServer; if (webServer == null) { // 从容器获取WebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // 创建WebServer实例 this.webServer = factory.getWebServer(getSelfInitializer()); } }5.2 Tomcat启动细节
以最常用的Tomcat为例:
- 创建
TomcatServletWebServerFactory实例 - 初始化Connector(默认8080端口)
- 加载所有
Servlet、Filter和Listener - 启动Tomcat服务
可以通过application.properties自定义配置:
server.port=9090 server.tomcat.max-threads=200 server.tomcat.accept-count=1006. 关键扩展点解析
6.1 初始化器(Initializer)
实现ApplicationContextInitializer接口可以在上下文刷新前执行自定义逻辑:
public class MyInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext context) { // 例如设置活跃profile context.getEnvironment().setActiveProfiles("dev"); } }需要在META-INF/spring.factories中注册:
org.springframework.context.ApplicationContextInitializer=com.example.MyInitializer6.2 监听器(Listener)
Spring Boot定义了丰富的生命周期事件:
public interface SpringApplicationRunListener { void starting(); // 启动开始 void environmentPrepared(ConfigurableEnvironment environment); // 环境准备完成 void contextPrepared(ConfigurableApplicationContext context); // 上下文准备 void contextLoaded(ConfigurableApplicationContext context); // 上下文加载 void started(ConfigurableApplicationContext context); // 启动完成 void running(ConfigurableApplicationContext context); // 运行中 void failed(ConfigurableApplicationContext context, Throwable exception); // 启动失败 }通过监听这些事件,我们可以实现诸如启动时数据初始化、监控上报等功能。
7. 实战中的常见问题
7.1 启动速度优化
在大型项目中,启动时间可能成为痛点。通过以下方法可以显著提升启动速度:
- 延迟初始化:
spring.main.lazy-initialization=true - 排除不必要的自动配置:
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) - 使用JVM参数:
-XX:TieredStopAtLevel=1
7.2 自动配置失效排查
当自动配置不生效时,可以:
- 开启debug日志:
logging.level.org.springframework.boot.autoconfigure=DEBUG - 检查条件注解:
@ConditionalOnClass要求的类是否存在 - 查看自动配置报告:
management.endpoint.conditions.enabled=true
8. 深度定制启动流程
8.1 自定义Banner
在resources目录下添加banner.txt:
___ ___ _ _ ___ / __| / __|| || | / __| \__ \| (__ | __ | \__ \ |___/ \___||_||_| |___/可以通过spring.banner.location指定自定义文件位置。
8.2 完全自定义SpringApplication
如果需要完全控制启动过程:
public static void main(String[] args) { SpringApplication app = new SpringApplication(MyApp.class); app.setBannerMode(Banner.Mode.OFF); // 关闭Banner app.setWebApplicationType(WebApplicationType.NONE); // 非Web应用 app.run(args); }在云原生场景下,这种定制非常有用。比如在Kubernetes中,我们可能需要区分就绪检查和存活检查的启动阶段。