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应用。SpringFactoriesLoader升级
2.x版本读取META-INF/spring.factories
文件使用Properties
加载,3.x版本引入了新的API(SpringFactoriesLoader.forDefaultResourceLocation()
),并且支持META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件,提高了加载性能。BootstrapContext增强
3.x版本中BootstrapContext
在启动阶段的使用更灵活,可以在应用启动前注册额外的组件。Jakarta命名空间迁移
所有与Servlet相关的类名由javax.*
迁移到jakarta.*
,这在3.x升级时需要特别注意。
示例:最简单的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 加载 ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 从 spring.factories 加载 ApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 推断并设置 main 方法所在类this.mainApplicationClass = deduceMainApplicationClass();
}
几个关键点:
推断 WebApplicationType
如果classpath中存在
DispatcherServlet
,就认为是Servlet类型应用;如果存在
DispatcherHandler
,就认为是Reactive类型应用;否则是普通的
NONE
类型(无Web环境)。
加载初始化器(Initializers)
通过
SpringFactoriesLoader
扫描META-INF/spring.factories
,自动实例化所有ApplicationContextInitializer
实现类。
加载监听器(Listeners)
同样用
SpringFactoriesLoader
机制加载ApplicationListener
实现类,用于监听应用启动过程的事件。
推断Main类
遍历当前线程的调用栈,找到第一个包含
main
方法的类。
示例:自定义ApplicationContextInitializer
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic 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();// 创建并配置 EnvironmentConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, args);// 打印 BannerBanner printedBanner = printBanner(environment);// 创建 ApplicationContextcontext = createApplicationContext();// 准备上下文prepareContext(bootstrapContext, context, environment, listeners, args, printedBanner);// 刷新上下文(加载Bean、启动内嵌容器)refreshContext(context);// 执行 CommandLineRunner、ApplicationRunnerafterRefresh(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
。
prepareContext
注册初始化器(Initializer)。
注册监听器(Listener)。
注册主配置类(启动类)。
refreshContext
调用Spring核心的
refresh()
方法,完成Bean的加载、依赖注入、事件注册等。
执行Runner回调
CommandLineRunner
和ApplicationRunner
用于启动完成后的业务处理。
触发应用就绪事件
发布
ApplicationReadyEvent
,代表应用完全启动并可对外提供服务。
示例:监听启动事件
@Component
public class MyStartupListener implements ApplicationListener<ApplicationStartingEvent> {@Overridepublic 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 {@Autowiredprivate Environment environment;@Overridepublic 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)
BeanPostProcessor
作用:增强 Bean 实例(代理、依赖注入等)
运行时机:Bean 实例化 前后
常见实现:
AutowiredAnnotationBeanPostProcessor
(处理 @Autowired)、AopProxyCreator
示例:自定义 BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {if (beanName.equals("myService")) {System.out.println("Before init: " + beanName);}return bean;}@Overridepublic 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@ConditionalOnMissingBeanpublic 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@ConditionalOnMissingBeanpublic 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 {@Autowiredprivate 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();
}
意思是:
如果你自己在配置类里声明了
ObjectMapper
Bean,那么 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) {// 构造器,必须包含这两个参数}@Overridepublic void starting() {System.out.println("应用开始启动");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {System.out.println("环境已准备好");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("上下文已准备好");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("上下文已加载");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("应用已启动");}@Overridepublic void running(ConfigurableApplicationContext context) {System.out.println("应用正在运行");}@Overridepublic 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> {@Overridepublic 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> {@Overridepublic 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@Lazypublic 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@Lazypublic ExpensiveBean expensiveBean() {System.out.println("初始化ExpensiveBean...");return new ExpensiveBean();}
}
运行该程序,可以看到启动阶段时间统计,并且expensiveBean
只有在首次调用时才初始化。
7.5 总结
通过
StopWatch
和--debug
日志,能有效定位启动耗时阶段。延迟加载、精准扫描、排除无用自动配置是主要优化手段。
优化启动性能有助于提升开发效率、加快部署速度、降低云环境资源消耗。