Spring Boot启动流程示例详解
1. Spring Boot启动流程概述
1.1 Spring Boot的核心特性
Spring Boot是Spring官方推出的一个快速构建独立运行、生产级别Spring应用的框架,它的核心目标是简化Spring应用的搭建与部署。
核心特性主要包括:
- 内嵌Web容器
- 内置Tomcat、Jetty、Undertow,无需单独部署WAR包到外部容器。只需运行
java -jar即可启动。 - 自动配置(Auto Configuration)
- 通过
@EnableAutoConfiguration结合条件化装配(Conditional注解)自动为应用注册Bean,大幅减少xml或Java配置的编写量。 - 独立运行(Standalone)
- 应用可以以一个独立的JAR包形式运行,避免复杂的服务器配置。
- 起步依赖(Starter Dependencies)
- 提供
spring-boot-starter-*依赖包,按需引入常用功能,减少手动管理依赖的复杂度。 - 生产环境特性
- 提供
Actuator监控、健康检查、环境信息、度量指标等。
类比:可以把Spring Boot想象成“开箱即用的开发平台”,你不需要自己组装各种组件,它已经帮你准备好了一整套“工具箱”和“默认配置”。
1.2 启动流程整体架构(文字描述)
Spring Boot启动的核心流程可以抽象成五个阶段:
- 初始化SpringApplication
- 解析启动类(
@SpringBootApplication标注的类) - 推断应用类型(
WebApplicationType) - 加载初始化器(
ApplicationContextInitializer) - 加载监听器(
ApplicationListener)
- 解析启动类(
- 准备运行环境(Environment)
- 创建并配置
Environment对象 - 加载外部配置(命令行参数、配置文件、系统属性等)
- 配置转换器(
PropertySource->Environment)
- 创建并配置
- 创建并刷新ApplicationContext
- 根据应用类型创建合适的
ApplicationContext(Servlet、Reactive或普通应用) - 注册Bean定义、执行
BeanFactoryPostProcessor和BeanPostProcessor - 启动内嵌Web容器(如Tomcat)
- 根据应用类型创建合适的
- 自动配置与Bean加载
- 解析
@EnableAutoConfiguration加载自动配置类 - 条件化装配判断是否加载特定Bean
- 初始化所有单例Bean
- 解析
- 完成启动并执行回调
- 发布
ApplicationReadyEvent事件 - 执行
CommandLineRunner和ApplicationRunner
- 发布
文字版的启动时序:
SpringApplication实例化 -> 推断Web类型 -> 加载监听器 -> 创建Environment -> 加载配置 -> 创建ApplicationContext -> 自动配置 -> 启动容器 -> 应用就绪
1.3 Spring Boot 2.x 与 3.x 启动机制差异
在Spring Boot 3.x(基于Spring Framework 6)中,启动流程的总体框架与2.x一致,但有一些细节变化:
- WebApplicationType推断优化
- Spring Boot 3.x在推断应用类型时,对
org.springframework.web.reactive.DispatcherHandler等类的检测进行了微调,更精确地区分Servlet与Reactive应用。
- Spring Boot 3.x在推断应用类型时,对
- SpringFactoriesLoader升级
- 2.x版本读取
META-INF/spring.factories文件使用Properties加载,3.x版本引入了新的API(SpringFactoriesLoader.forDefaultResourceLocation()),并且支持META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,提高了加载性能。
- 2.x版本读取
- BootstrapContext增强
- 3.x版本中
BootstrapContext在启动阶段的使用更灵活,可以在应用启动前注册额外的组件。
- 3.x版本中
- Jakarta命名空间迁移
- 所有与Servlet相关的类名由
javax.*迁移到jakarta.*,这在3.x升级时需要特别注意。
- 所有与Servlet相关的类名由
示例:最简单的Spring Boot引导类
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @SpringBootApplication 是三个注解的组合:
* - @Configuration:表示该类是一个配置类
* - @EnableAutoConfiguration:开启自动配置
* - @ComponentScan:开启包扫描
*/
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// run方法会启动整个Spring Boot应用
SpringApplication.run(DemoApplication.class, args);
}
}运行上面的程序,你会在控制台看到类似输出(简化版):
:: Spring Boot :: (v3.2.0)
2025-08-11 10:15:00 INFO --- Starting DemoApplication on localhost with PID 12345
2025-08-11 10:15:01 INFO --- Tomcat started on port(s): 8080 (http)
2025-08-11 10:15:01 INFO --- Started DemoApplication in 1.234 seconds
2. Spring Boot启动的核心阶段
2.1 SpringApplication初始化阶段
当你调用:
SpringApplication.run(DemoApplication.class, args);
实际上会先执行 new SpringApplication(primarySources...) 构造方法,再执行 .run(args) 方法。
源码入口(Spring Boot 2.x / 3.x)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用类型(Servlet / Reactive / None)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从 spring.factories 加载 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从 spring.factories 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断并设置 main 方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
}几个关键点:
- 推断 WebApplicationType
- 如果classpath中存在
DispatcherServlet,就认为是Servlet类型应用; - 如果存在
DispatcherHandler,就认为是Reactive类型应用; - 否则是普通的
NONE类型(无Web环境)。
- 如果classpath中存在
- 加载初始化器(Initializers)
- 通过
SpringFactoriesLoader扫描META-INF/spring.factories,自动实例化所有ApplicationContextInitializer实现类。
- 通过
- 加载监听器(Listeners)
- 同样用
SpringFactoriesLoader机制加载ApplicationListener实现类,用于监听应用启动过程的事件。
- 同样用
- 推断Main类
- 遍历当前线程的调用栈,找到第一个包含
main方法的类。
- 遍历当前线程的调用栈,找到第一个包含
示例:自定义ApplicationContextInitializer
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("MyInitializer: 应用上下文初始化中...");
}
}
resources/META-INF/spring.factories:
org.springframework.context.ApplicationContextInitializer=\ com.example.demo.MyInitializer
启动时会先输出:
MyInitializer: 应用上下文初始化中...
说明在SpringApplication构造时已成功加载该初始化器。
2.2 SpringApplication.run()执行过程全解析
SpringApplication.run() 是整个启动的核心,下面是简化后的源码执行顺序(以3.x为例):
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 启动监听器集合
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
// 初始化引导上下文(BootstrapContext)
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 创建并配置 Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, args);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// 创建 ApplicationContext
context = createApplicationContext();
// 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, args, printedBanner);
// 刷新上下文(加载Bean、启动内嵌容器)
refreshContext(context);
// 执行 CommandLineRunner、ApplicationRunner
afterRefresh(context, args);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
listeners.running(context);
return context;
}分步骤解析:
- StopWatch计时器
- 用于记录启动耗时,最终会在日志输出
Started Xxx in 2.345 seconds。
- 用于记录启动耗时,最终会在日志输出
- 启动监听器(SpringApplicationRunListeners)
- 触发
ApplicationStartingEvent事件,可以在这里做日志或系统初始化。
- 触发
- 创建BootstrapContext
- 启动早期用于注册额外组件,如配置加载器(Spring Cloud 会用到)。
- 创建并准备Environment
- 加载外部配置(命令行、配置文件、环境变量)。
- 设置
PropertySource的加载顺序。
- 打印Banner
- 控制台输出Spring Boot ASCII艺术字,可以自定义。
- 创建ApplicationContext
- 根据Web类型创建
AnnotationConfigServletWebServerApplicationContext或AnnotationConfigApplicationContext。
- 根据Web类型创建
- prepareContext
- 注册初始化器(Initializer)。
- 注册监听器(Listener)。
- 注册主配置类(启动类)。
- refreshContext
- 调用Spring核心的
refresh()方法,完成Bean的加载、依赖注入、事件注册等。
- 调用Spring核心的
- 执行Runner回调
CommandLineRunner和ApplicationRunner用于启动完成后的业务处理。
- 触发应用就绪事件
- 发布
ApplicationReadyEvent,代表应用完全启动并可对外提供服务。
- 发布
示例:监听启动事件
@Component
public class MyStartupListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
System.out.println("应用启动中:执行早期初始化逻辑...");
}
}2.3 WebApplicationType推断机制
源码(简化):
public static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(REACTIVE_DISPATCHER, null) && !ClassUtils.isPresent(DISPATCHER_SERVLET, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}判断逻辑:
- 如果存在
org.springframework.web.reactive.DispatcherHandler且不存在DispatcherServlet→ REACTIVE - 如果缺少Servlet必要类 → NONE
- 其他情况默认SERVLET
💡 小结:
SpringApplication构造阶段主要是推断类型、加载初始化器、监听器。run()方法则是准备环境、创建上下文、刷新Bean、执行回调的全过程。- Web类型推断保证了Spring Boot能自动适配不同类型的应用(Servlet、Reactive、None)。
3. 环境准备与配置加载
3.1 Environment对象的创建与类型
在 Spring Boot 启动时,会根据应用类型(WebApplicationType)来创建不同的 Environment 实现类:
| 应用类型 | Environment实现类 |
|---|---|
| SERVLET | StandardServletEnvironment |
| REACTIVE | StandardReactiveWebEnvironment |
| NONE | StandardEnvironment |
源码位置(SpringApplication.prepareEnvironment):
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}Environment 作用:
- 存放应用的配置源(
PropertySource)。 - 提供配置值的统一获取接口(
getProperty)。 - 提供 profile 管理(
setActiveProfiles)。
小示例:获取Environment对象
@Component
public class EnvPrinter implements CommandLineRunner {
@Autowired
private Environment environment;
@Override
public void run(String... args) {
System.out.println("当前激活的profile: " + Arrays.toString(environment.getActiveProfiles()));
System.out.println("server.port=" + environment.getProperty("server.port"));
}
}运行时会输出配置文件或命令行参数中对应的值。
3.2 配置源加载顺序与优先级
Spring Boot 的配置加载是有优先级顺序的,后加载的会覆盖先加载的。
官方的 优先级顺序(从高到低):
- 命令行参数
--server.port=9000
- Java系统属性
System.setProperty("server.port", "9000")
- 操作系统环境变量
application-{profile}.properties(当前激活的 profile 配置)application.properties/application.yml
- 打包在 jar 外部的配置文件(优先于 jar 内部配置文件)
- 打包在 jar 内部的配置文件
- 默认配置(代码中
SpringApplication.setDefaultProperties()设置)
- 默认配置(代码中
命令行参数覆盖示例
假设 application.properties 中配置:
server.port=8080
运行:
java -jar demo.jar --server.port=9001
最终生效端口是 9001,因为命令行参数优先级最高。
3.3 application.properties 与 application.yml 加载规则
Spring Boot 支持 .properties 和 .yml 两种格式,加载顺序(同一位置)是:
- 先加载
.properties - 再加载
.yml
并且,配置文件可以放在多个位置:
classpath:/classpath:/config/file:./file:./config/
优先级顺序(从高到低):
file:./config/file:./classpath:/config/classpath:/
3.4 命令行参数、系统属性覆盖规则
Spring Boot 会自动将命令行参数解析成 PropertySource 并加入 Environment,它们的优先级高于配置文件。
源码位置:
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
new CommandLinePropertySource<>("commandLineArgs", args);
}命令行参数会被解析成 key-value 形式,覆盖之前加载的同名配置。
3.5 @Value 与 @ConfigurationProperties 获取配置
@Value
直接从 Environment 中取值:
@Value("${server.port}")
private int port;
@ConfigurationProperties
批量绑定配置到 Java Bean:
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppConfig {
private String name;
private int timeout;
// getters and setters
}application.properties:
myapp.name=DemoApp myapp.timeout=30
这样 MyAppConfig 会自动绑定配置值。
💡 小结:
Environment是配置的“中央仓库”。- 配置源加载是分阶段进行的,且有明确的优先级。
- 命令行参数几乎总是能覆盖所有配置文件值。
@Value适合单个配置值,@ConfigurationProperties适合批量绑定。
4. ApplicationContext的创建与刷新
4.1 ApplicationContext 的类型选择
在 SpringApplication.createApplicationContext() 中,会根据 WebApplicationType 选择不同的 ApplicationContext 实现:
| 应用类型 | ApplicationContext 实现类 |
|---|---|
| SERVLET | AnnotationConfigServletWebServerApplicationContext |
| REACTIVE | AnnotationConfigReactiveWebServerApplicationContext |
| NONE | AnnotationConfigApplicationContext |
源码(简化):
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(
"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName(
"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName(
"org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable to create ApplicationContext", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
重点:
- SERVLET 类型不仅是 IoC 容器,还会管理内嵌的 Servlet Web 服务器(Tomcat/Jetty/Undertow)。
- REACTIVE 类型则会启动 Netty 作为反应式 Web 服务器。
- NONE 类型适用于纯后端任务(无Web功能)。
4.2 refreshContext() 核心逻辑解析
refreshContext() 会调用 Spring Framework 的 AbstractApplicationContext.refresh(),这是 IoC 容器生命周期的核心方法。
简化执行步骤(以 Servlet 应用为例):
- 准备刷新上下文
- 初始化属性源(PropertySources)
- 验证必需的属性是否存在
- 获取并准备 BeanFactory
- 创建
DefaultListableBeanFactory
- 创建
- 执行 BeanFactoryPostProcessor
- 在 Bean 定义加载后、实例化前执行
- 注册 BeanPostProcessor
- 在 Bean 实例化前后进行增强(如 @Autowired 注入、AOP 代理)
- 初始化消息源(MessageSource)
- 用于国际化
- 初始化事件广播器(ApplicationEventMulticaster)
- 创建内嵌 Web 服务器(ServletWebServerFactory)并启动
- 实例化剩余的单例 Bean
- 完成刷新
- 发布
ContextRefreshedEvent
- 发布
4.3 BeanFactoryPostProcessor 与 BeanPostProcessor 执行顺序
很多扩展功能(比如 MyBatis、Spring Cloud)都是依赖这两个接口来修改 Bean 定义或 Bean 实例的。
执行顺序:
- BeanFactoryPostProcessor
- 作用:修改 Bean 定义元数据(
BeanDefinition) - 运行时机:Bean 实例化 之前
- 常见实现:
ConfigurationClassPostProcessor(处理 @Configuration、@Bean)
- 作用:修改 Bean 定义元数据(
- BeanPostProcessor
- 作用:增强 Bean 实例(代理、依赖注入等)
- 运行时机:Bean 实例化 前后
- 常见实现:
AutowiredAnnotationBeanPostProcessor(处理 @Autowired)、AopProxyCreator
示例:自定义 BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (beanName.equals("myService")) {
System.out.println("Before init: " + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (beanName.equals("myService")) {
System.out.println("After init: " + beanName);
}
return bean;
}
}4.4 BootstrapContext 的作用
在 Spring Boot 2.4 之后引入 BootstrapContext,它是应用启动早期的一个轻量级上下文,用于注册启动前所需的组件,典型用途是:
- 注册配置加载器(Spring Cloud Config 使用)
- 注册早期的监控或指标收集组件
源码片段:
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ApplicationEnvironmentPreparedEvent event =
new ApplicationEnvironmentPreparedEvent(this, args, environment);
listeners.environmentPrepared(bootstrapContext, environment);
💡 小结:
- ApplicationContext 是整个应用的核心容器,负责 Bean 生命周期、事件、资源管理。
- refresh() 是启动过程中 Bean 加载的核心方法。
- BeanFactoryPostProcessor 改定义,BeanPostProcessor 改实例。
- BootstrapContext 在早期阶段提供了扩展能力。
5. 自动配置与条件化装配
Spring Boot最大的亮点之一,就是它的自动配置(Auto-Configuration)机制——你只需要引入依赖,不用手动配置,相关的Bean就会自动帮你注册好。
这一章我们会拆开它的底层原理,逐步分析:
5.1 @EnableAutoConfiguration 原理
在Spring Boot应用的启动类中,我们经常会看到:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@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 {
// ...
}
核心就是 @EnableAutoConfiguration。
源码入口
@EnableAutoConfiguration 本身也只是一个注解,它通过 @Import 引入了 AutoConfigurationImportSelector:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
重点来了:
@Import会把AutoConfigurationImportSelector这个类加入到容器启动过程中。- 这个类会扫描所有
META-INF/spring.factories中声明的自动配置类。
5.2 SpringFactoriesLoader 与 META-INF/spring.factories
SpringFactoriesLoader 是一个帮助类,负责加载 META-INF/spring.factories 文件中定义的类名列表。
典型的 spring.factories 文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
工作流程:
SpringFactoriesLoader.loadFactoryNames()会扫描所有依赖包下的META-INF/spring.factories文件。- 读取 key 为
org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置值。 - 把这些类(通常以
xxxAutoConfiguration结尾)交给Spring容器去加载。
源码关键点(SpringFactoriesLoader)
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 这里的
factoryType就是EnableAutoConfiguration.class - 最终得到一个自动配置类的全限定名列表
5.3 条件化注解(@ConditionalXXX)
自动配置类并不是无条件生效的,它们会结合一系列条件注解判断当前环境是否满足,再决定是否注册Bean。
常见的条件注解:
| 注解 | 作用 |
|---|---|
@ConditionalOnClass | 当类路径存在指定类时生效 |
@ConditionalOnMissingClass | 当类路径不存在指定类时生效 |
@ConditionalOnBean | 当容器中存在指定Bean时生效 |
@ConditionalOnMissingBean | 当容器中不存在指定Bean时生效 |
@ConditionalOnProperty | 当配置文件中存在某个属性并且值匹配时生效 |
@ConditionalOnWebApplication | 当应用是Web类型时生效 |
@ConditionalOnNotWebApplication | 当应用不是Web类型时生效 |
@ConditionalOnExpression | 根据SpEL表达式结果决定是否生效 |
例子(JacksonAutoConfiguration):
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@EnableConfigurationProperties(JacksonProperties.class)
public class JacksonAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}
}解释:
- 如果Classpath中有
ObjectMapper类(Jackson库),则配置生效。 - 如果容器中没有
ObjectMapper,则自动注册一个。
5.4 自定义自动配置类实战
假设我们想在Spring Boot启动时,自动配置一个 HelloService:
1. 编写服务类
public class HelloService {
public String sayHello(String name) {
return "Hello, " + name;
}
}
2. 编写自动配置类
@Configuration
@ConditionalOnClass(HelloService.class)
public class HelloServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HelloService helloService() {
return new HelloService();
}
}3. 配置 META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.config.HelloServiceAutoConfiguration
4. 引入依赖后直接使用
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("/hello")
public String hello(String name) {
return helloService.sayHello(name);
}
}无需额外配置,访问 /hello?name=Boot 即可看到自动配置效果。
5.5 条件注解(@ConditionalXXX)的源码执行流程
Spring Boot 的自动配置类之所以能“按需生效”,核心就是依赖 Spring Framework 提供的 条件化装配机制,也就是 @Conditional。
5.5.1 条件注解的继承关系
所有 @ConditionalOnXxx 注解,最终都是 @Conditional 的语法糖。
举个例子:
@ConditionalOnClass(ObjectMapper.class)
展开看源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
可以看到:
- 它其实就是
@Conditional,只不过指定了一个Condition实现类:OnClassCondition。 - 也就是说,
@ConditionalOnClass-> OnClassCondition -> 判断classpath是否存在指定类。
5.5.2 条件评估流程
条件注解是在 Spring 容器启动阶段,由 ConfigurationClassPostProcessor 解析 @Configuration 类时进行评估的。
流程大致是:
- 扫描到自动配置类
ConfigurationClassParser解析自动配置类上的注解。
- 遇到 @Conditional
- 调用
ConditionEvaluator.shouldSkip()方法。
- 调用
- 找到对应 Condition 实现类
- 比如
OnClassCondition,会去执行matches()方法。
- 比如
- 返回 true/false
true表示条件满足 -> 该配置类或方法会注册到容器false表示条件不满足 -> 直接跳过
源码片段(ConditionEvaluator)
public boolean shouldSkip(AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata.isAnnotated(Conditional.class.getName())) {
List<Condition> conditions = getConditionClasses(metadata);
for (Condition condition : conditions) {
if (!condition.matches(context, metadata)) {
return true; // 跳过
}
}
}
return false;
}
重点:
matches()方法是判断条件是否成立的地方。- 每个
@ConditionalOnXxx注解都有自己的 Condition 实现类。
5.5.3 常见 Condition 实现类
| 条件注解 | 对应 Condition 类 | 作用 |
|---|---|---|
@ConditionalOnClass | OnClassCondition | 检查类路径是否存在指定类 |
@ConditionalOnMissingClass | OnClassCondition | 检查类路径是否不存在指定类 |
@ConditionalOnBean | OnBeanCondition | 检查容器中是否存在指定 Bean |
@ConditionalOnMissingBean | OnBeanCondition | 检查容器中是否不存在指定 Bean |
@ConditionalOnProperty | OnPropertyCondition | 检查配置文件中属性是否满足 |
@ConditionalOnWebApplication | OnWebApplicationCondition | 检查应用是否是 Web 环境 |
@ConditionalOnExpression | OnExpressionCondition | 执行 SpEL 表达式并判断结果 |
5.5.4 执行顺序与调试技巧
- 条件判断会在 BeanDefinition 注册阶段执行,而不是Bean实例化阶段。
- 你可以在
Condition.matches()方法里打断点,启动项目,就能看到哪些条件返回 false,从而排查自动配置没生效的原因。 - Spring Boot 提供了一个
--debug启动参数,可以打印出条件评估报告。
命令行启动
java -jar demo.jar --debug
部分输出
=========================
CONDITIONS EVALUATION REPORT
=========================
Positive matches:
-----------------
JacksonAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
Negative matches:
-----------------
DataSourceAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.sql.DataSource' (OnClassCondition)这样你就能非常清楚地知道某个自动配置类为什么没生效。
5.6 自动配置的优先级与覆盖规则
Spring Boot 设计的自动配置是低优先级的,这样开发者可以随时覆盖默认配置。
5.6.1 自动配置的核心原则
- 用户优先(开发者自己配置的 Bean 优先于 Spring Boot 提供的 Bean)
- 条件触发(只有在条件成立时,自动配置才会生效)
- 可覆盖可禁用(你可以通过配置文件或注解控制自动配置是否启用)
5.6.2 自动配置的低优先级实现方式
1.@ConditionalOnMissingBean
- 大多数自动配置类在注册 Bean 时都会加上这个条件。
- 表示:如果容器中已经有这个 Bean,就不再创建默认的 Bean。
示例:
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
意思是:
- 如果你自己在配置类里声明了
ObjectMapperBean,那么 Boot 的这个方法不会执行。
2.@AutoConfigureBefore/@AutoConfigureAfter
- 用来指定自动配置类之间的顺序。
- 比如:
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class MyCustomJsonConfig { ... }
这样你的配置会在 JacksonAutoConfiguration 之前执行。
3.@Primary
- 如果你想让多个同类型 Bean 中某一个优先被注入,可以加上
@Primary。 - 这不是自动配置特有的,但常用于覆盖默认 Bean。
4.spring.main.allow-bean-definition-overriding
- Spring Boot 2.1 以后默认不允许 Bean 覆盖,如果你声明了和默认配置同名的 Bean 会报错。
- 解决办法:
spring:
main:
allow-bean-definition-overriding: true这样你的 Bean 会覆盖默认 Bean。
5.6.3 禁用某个自动配置
有三种常用方式:
方式 1:@SpringBootApplication(exclude = ...)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyApp {}
方式 2:配置文件
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration方式 3:META-INF/spring.factories
- 这种是你自己写 starter 时用的,普通项目一般不直接操作。
5.6.4 调试 Bean 覆盖问题
- 启动加
--debug看条件评估报告。 - 使用
ApplicationContext打印所有 Bean:
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
如果报 BeanDefinitionOverrideException,先确认是否是自己命名冲突,必要时允许覆盖。
6. Spring Boot启动流程的扩展点
Spring Boot的设计理念之一是高度的可扩展性。在启动过程中,Spring Boot提供了丰富的扩展点,允许开发者在不同阶段插入自定义逻辑,满足多样化的业务需求。这一章将详细介绍Spring Boot启动流程中常用的扩展点,包括SpringApplicationRunListener、ApplicationListener以及SpringApplicationBuilder的链式启动配置。
6.1 SpringApplicationRunListener —— 启动事件监听器
6.1.1 作用与触发时机
SpringApplicationRunListener接口用于监听Spring Boot启动流程的各个关键阶段。它的核心目的是在启动流程的不同时间点触发回调,帮助开发者插入自定义操作或日志打印等辅助功能。
Spring Boot启动时会依次调用SpringApplicationRunListener中的如下事件方法:
starting():启动初始化,Spring Boot启动的第一个阶段。environmentPrepared(ConfigurableEnvironment environment):环境准备完成,配置已加载。contextPrepared(ConfigurableApplicationContext context):ApplicationContext创建完成但尚未刷新。contextLoaded(ConfigurableApplicationContext context):ApplicationContext加载完成。started(ConfigurableApplicationContext context):ApplicationContext刷新完成。running(ConfigurableApplicationContext context):应用准备就绪,进入运行状态。failed(ConfigurableApplicationContext context, Throwable exception):启动失败回调。
6.1.2 Spring Boot内部实现及自动加载
Spring Boot通过META-INF/spring.factories文件自动加载所有注册的SpringApplicationRunListener实现。常见的内置实现包括:
EventPublishingRunListener:负责将启动事件发布到Spring事件机制。ClasspathLoggingApplicationListener:启动时打印类路径信息。StartupInfoLogger:打印启动信息统计。
自动加载示例(spring.factories文件):
org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener
6.1.3 自定义 SpringApplicationRunListener
开发者可以通过实现SpringApplicationRunListener接口来自定义启动事件监听器。实现步骤:
- 实现接口并提供带
SpringApplication和String[] args构造函数。 - 在
META-INF/spring.factories注册监听器类。
示例代码:
public class MyRunListener implements SpringApplicationRunListener {
public MyRunListener(SpringApplication app, String[] args) {
// 构造器,必须包含这两个参数
}
@Override
public void starting() {
System.out.println("应用开始启动");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("环境已准备好");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("上下文已准备好");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("上下文已加载");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("应用已启动");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("应用正在运行");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("应用启动失败,异常信息:" + exception.getMessage());
}
}注册监听器:
org.springframework.boot.SpringApplicationRunListener=com.example.MyRunListener
6.2 ApplicationListener —— Spring事件监听器
6.2.1 作用与应用场景
ApplicationListener是Spring框架的核心事件监听接口,支持监听包括启动事件在内的所有Spring事件。它基于Spring的事件发布机制,允许开发者对特定事件做出响应。
相比SpringApplicationRunListener,ApplicationListener更通用,不仅限于启动阶段事件,还可监听应用运行过程中的各种事件。
6.2.2 常见启动事件类型
| 事件类 | 触发时机 |
|---|---|
ApplicationStartingEvent | Spring Boot启动最开始时 |
ApplicationEnvironmentPreparedEvent | 环境准备完成时 |
ApplicationPreparedEvent | ApplicationContext创建完毕,准备刷新 |
ApplicationStartedEvent | ApplicationContext刷新成功 |
ApplicationReadyEvent | 应用启动完成,准备接收请求 |
ApplicationFailedEvent | 应用启动失败 |
6.2.3 自定义ApplicationListener示例
@Component
public class MyReadyListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("应用已准备就绪,执行自定义初始化操作...");
// 例如启动缓存预热、消息监听器等
}
}6.3 SpringApplicationBuilder —— 链式启动配置工具
6.3.1 设计理念与优势
SpringApplicationBuilder是构建SpringApplication实例的链式构建器,支持更灵活、优雅的启动配置,适合复杂启动场景。
使用SpringApplicationBuilder,可以逐步叠加各种配置,如初始化器、监听器、激活Profile等,并最后调用run()方法启动应用。
6.3.2 常用API与示例
sources(Class<?>... sources):设置主配置类或额外配置类。initializers(ApplicationContextInitializer<?>... initializers):添加上下文初始化器。listeners(ApplicationListener<?>... listeners):添加事件监听器。profiles(String... profiles):激活指定环境。run(String... args):启动应用。
示例:
new SpringApplicationBuilder()
.sources(MyApplication.class)
.initializers(new MyContextInitializer())
.listeners(new MyReadyListener())
.profiles("dev")
.run(args);
6.3.3 自定义ApplicationContextInitializer示例
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContext初始化逻辑...");
// 可以动态注册Bean、修改环境变量等
}
}6.4 小结
- Spring Boot启动流程通过
SpringApplicationRunListener提供了丰富的启动阶段监听能力,适合执行启动监控、日志收集、资源准备等任务。 ApplicationListener基于Spring事件机制,支持对启动事件及其他业务事件的监听,更加通用。SpringApplicationBuilder是链式构建启动参数的工具,极大提升了启动配置的灵活性和可维护性。
通过掌握这几个核心扩展点,开发者可以对Spring Boot启动流程进行高度自定义,满足各种复杂的启动需求,提升应用的健壮性和扩展能力。
7. Spring Boot启动性能优化
随着企业级应用复杂度的提升,Spring Boot应用的启动时间成为一个重要考量指标。快速启动不仅提升开发效率,还能改善云环境中的弹性扩缩容体验。此章将围绕启动性能展开,讲解启动耗时分析方法、常见瓶颈,以及针对性的优化策略。
7.1 启动耗时分析:利用StopWatch
7.1.1 StopWatch在Spring Boot中的应用
在SpringApplication.run()方法中,Spring Boot通过org.springframework.util.StopWatch来度量启动各个阶段的耗时,帮助开发者定位瓶颈。
简化代码片段(摘自SpringApplication.run()):
StopWatch stopWatch = new StopWatch();
stopWatch.start("SpringApplication启动");
// 启动流程中的重要阶段代码...
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());StopWatch会输出类似如下的阶段耗时报告:
StopWatch '': running time = 12345 ns
---------------------------------------------
ns % Task name
---------------------------------------------
5000000 40% SpringApplication启动
7500000 60% ApplicationContext刷新
7.1.2 使用启动日志和--debug参数定位耗时
Spring Boot支持通过命令行参数--debug开启详细日志,打印启动过程中的细节信息,便于发现耗时较长的组件或阶段。
java -jar myapp.jar --debug
日志中会包含大量有用信息,如自动配置条件匹配、Bean创建时间等。
7.2 主要启动性能瓶颈分析
- 自动配置扫描与条件判断:复杂的
@Conditional注解逻辑,条件不匹配时仍需执行判断,影响启动速度。 - Bean实例化和依赖注入:大量单例Bean的创建、复杂依赖关系的解析耗时较长。
- 环境变量和配置加载:读取多层配置文件和系统属性,尤其是网络配置(如远程配置中心)可能阻塞启动。
- 类路径扫描与组件扫描:扫描大量包及类时会增加启动负担。
- 嵌入式容器启动:内嵌Tomcat、Jetty或Undertow启动耗时不可忽视。
7.3 启动性能优化策略
7.3.1 使用@Lazy延迟加载Bean
通过给不必要立即加载的Bean添加@Lazy注解,Spring容器会延迟初始化,减少启动时的Bean加载压力。
示例:
@Configuration
public class MyConfig {
@Bean
@Lazy
public HeavyService heavyService() {
return new HeavyService();
}
}7.3.2 精准配置@ComponentScan扫描范围
缩小@ComponentScan的扫描范围,避免扫描无关包,减少类路径扫描时间。
@SpringBootApplication(scanBasePackages = "com.example.myapp.service")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
7.3.3 禁用不必要的自动配置
通过@SpringBootApplication(exclude = {AutoConfigurationClass.class})排除不需要的自动配置类,避免无用配置初始化。
示例:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication { ... }
7.3.4 自定义自动配置条件减少启动判断
简化自动配置中的@Conditional注解判断逻辑,避免复杂条件链。
7.3.5 采用Spring Boot Layered JAR优化启动
Spring Boot 2.3+支持分层Jar结构,将应用、依赖、启动器分开打包,有助于加速Docker镜像启动。
7.4 实践示例:启动时间统计与延迟加载
@SpringBootApplication
public class PerformanceApp {
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start("启动应用");
SpringApplication app = new SpringApplication(PerformanceApp.class);
app.run(args);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
@Bean
@Lazy
public ExpensiveBean expensiveBean() {
System.out.println("初始化ExpensiveBean...");
return new ExpensiveBean();
}
}运行该程序,可以看到启动阶段时间统计,并且expensiveBean只有在首次调用时才初始化。
7.5 总结
- 通过
StopWatch和--debug日志,能有效定位启动耗时阶段。 - 延迟加载、精准扫描、排除无用自动配置是主要优化手段。
- 优化启动性能有助于提升开发效率、加快部署速度、降低云环境资源消耗。
到此这篇关于Spring Boot启动流程详解的文章就介绍到这了,更多相关Spring Boot启动流程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
使用Java的Spring框架编写第一个程序Hellow world
这篇文章主要介绍了Java的Spring框架并用其开始编写第一个程序Hellow world的方法,Spring是Java的SSH三大web开发框架之一,需要的朋友可以参考下2015-12-12
java -jar命令运行 jar包时运行外部依赖jar包的场景分析
这篇文章主要介绍了java -jar命令运行 jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧2025-06-06


最新评论