当前位置: 首页 > news >正文

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启动的核心流程可以抽象成五个阶段:

  1. 初始化SpringApplication

    • 解析启动类(@SpringBootApplication标注的类)

    • 推断应用类型(WebApplicationType

    • 加载初始化器(ApplicationContextInitializer

    • 加载监听器(ApplicationListener

  2. 准备运行环境(Environment)

    • 创建并配置Environment对象

    • 加载外部配置(命令行参数、配置文件、系统属性等)

    • 配置转换器(PropertySource -> Environment

  3. 创建并刷新ApplicationContext

    • 根据应用类型创建合适的ApplicationContext(Servlet、Reactive或普通应用)

    • 注册Bean定义、执行BeanFactoryPostProcessorBeanPostProcessor

    • 启动内嵌Web容器(如Tomcat)

  4. 自动配置与Bean加载

    • 解析@EnableAutoConfiguration加载自动配置类

    • 条件化装配判断是否加载特定Bean

    • 初始化所有单例Bean

  5. 完成启动并执行回调

    • 发布ApplicationReadyEvent事件

    • 执行CommandLineRunnerApplicationRunner

文字版的启动时序:
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();
}

几个关键点:

  1. 推断 WebApplicationType

    • 如果classpath中存在DispatcherServlet,就认为是Servlet类型应用;

    • 如果存在DispatcherHandler,就认为是Reactive类型应用;

    • 否则是普通的NONE类型(无Web环境)。

  2. 加载初始化器(Initializers)

    • 通过SpringFactoriesLoader扫描META-INF/spring.factories,自动实例化所有ApplicationContextInitializer实现类。

  3. 加载监听器(Listeners)

    • 同样用SpringFactoriesLoader机制加载ApplicationListener实现类,用于监听应用启动过程的事件。

  4. 推断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;
}

分步骤解析:

  1. StopWatch计时器

    • 用于记录启动耗时,最终会在日志输出Started Xxx in 2.345 seconds

  2. 启动监听器(SpringApplicationRunListeners)

    • 触发ApplicationStartingEvent事件,可以在这里做日志或系统初始化。

  3. 创建BootstrapContext

    • 启动早期用于注册额外组件,如配置加载器(Spring Cloud 会用到)。

  4. 创建并准备Environment

    • 加载外部配置(命令行、配置文件、环境变量)。

    • 设置PropertySource的加载顺序。

  5. 打印Banner

    • 控制台输出Spring Boot ASCII艺术字,可以自定义。

  6. 创建ApplicationContext

    • 根据Web类型创建AnnotationConfigServletWebServerApplicationContextAnnotationConfigApplicationContext

  7. prepareContext

    • 注册初始化器(Initializer)。

    • 注册监听器(Listener)。

    • 注册主配置类(启动类)。

  8. refreshContext

    • 调用Spring核心的refresh()方法,完成Bean的加载、依赖注入、事件注册等。

  9. 执行Runner回调

    • CommandLineRunnerApplicationRunner用于启动完成后的业务处理。

  10. 触发应用就绪事件

    • 发布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实现类
SERVLETStandardServletEnvironment
REACTIVEStandardReactiveWebEnvironment
NONEStandardEnvironment

源码位置(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 的配置加载是有优先级顺序的,后加载的会覆盖先加载的。

官方的 优先级顺序(从高到低)

  1. 命令行参数

    • --server.port=9000

  2. Java系统属性

    • System.setProperty("server.port", "9000")

  3. 操作系统环境变量

  4. application-{profile}.properties(当前激活的 profile 配置)

  5. application.properties / application.yml

  6. 打包在 jar 外部的配置文件(优先于 jar 内部配置文件)

  7. 打包在 jar 内部的配置文件

  8. 默认配置(代码中 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/

优先级顺序(从高到低)

  1. file:./config/

  2. file:./

  3. classpath:/config/

  4. 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 实现类
SERVLETAnnotationConfigServletWebServerApplicationContext
REACTIVEAnnotationConfigReactiveWebServerApplicationContext
NONEAnnotationConfigApplicationContext

源码(简化)

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 应用为例):

  1. 准备刷新上下文

    • 初始化属性源(PropertySources)

    • 验证必需的属性是否存在

  2. 获取并准备 BeanFactory

    • 创建 DefaultListableBeanFactory

  3. 执行 BeanFactoryPostProcessor

    • 在 Bean 定义加载后、实例化前执行

  4. 注册 BeanPostProcessor

    • 在 Bean 实例化前后进行增强(如 @Autowired 注入、AOP 代理)

  5. 初始化消息源(MessageSource)

    • 用于国际化

  6. 初始化事件广播器(ApplicationEventMulticaster)

  7. 创建内嵌 Web 服务器(ServletWebServerFactory)并启动

  8. 实例化剩余的单例 Bean

  9. 完成刷新

    • 发布 ContextRefreshedEvent


4.3 BeanFactoryPostProcessor 与 BeanPostProcessor 执行顺序

很多扩展功能(比如 MyBatis、Spring Cloud)都是依赖这两个接口来修改 Bean 定义或 Bean 实例的。

执行顺序

  1. BeanFactoryPostProcessor

    • 作用:修改 Bean 定义元数据(BeanDefinition

    • 运行时机:Bean 实例化 之前

    • 常见实现:ConfigurationClassPostProcessor(处理 @Configuration、@Bean)

  2. 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

工作流程:

  1. SpringFactoriesLoader.loadFactoryNames() 会扫描所有依赖包下的 META-INF/spring.factories 文件。

  2. 读取 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的配置值。

  3. 把这些类(通常以 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 类时进行评估的。

流程大致是:

  1. 扫描到自动配置类

    • ConfigurationClassParser 解析自动配置类上的注解。

  2. 遇到 @Conditional

    • 调用 ConditionEvaluator.shouldSkip() 方法。

  3. 找到对应 Condition 实现类

    • 比如 OnClassCondition,会去执行 matches() 方法。

  4. 返回 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 类作用
@ConditionalOnClassOnClassCondition检查类路径是否存在指定类
@ConditionalOnMissingClassOnClassCondition检查类路径是否不存在指定类
@ConditionalOnBeanOnBeanCondition检查容器中是否存在指定 Bean
@ConditionalOnMissingBeanOnBeanCondition检查容器中是否不存在指定 Bean
@ConditionalOnPropertyOnPropertyCondition检查配置文件中属性是否满足
@ConditionalOnWebApplicationOnWebApplicationCondition检查应用是否是 Web 环境
@ConditionalOnExpressionOnExpressionCondition执行 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 自动配置的核心原则

  1. 用户优先(开发者自己配置的 Bean 优先于 Spring Boot 提供的 Bean)

  2. 条件触发(只有在条件成立时,自动配置才会生效)

  3. 可覆盖可禁用(你可以通过配置文件或注解控制自动配置是否启用)


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 覆盖问题

  1. 启动加 --debug 看条件评估报告。

  2. 使用 ApplicationContext 打印所有 Bean:

    Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
    
  3. 如果报 BeanDefinitionOverrideException,先确认是否是自己命名冲突,必要时允许覆盖。

6. Spring Boot启动流程的扩展点

Spring Boot的设计理念之一是高度的可扩展性。在启动过程中,Spring Boot提供了丰富的扩展点,允许开发者在不同阶段插入自定义逻辑,满足多样化的业务需求。这一章将详细介绍Spring Boot启动流程中常用的扩展点,包括SpringApplicationRunListenerApplicationListener以及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接口来自定义启动事件监听器。实现步骤:

  • 实现接口并提供带SpringApplicationString[] 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的事件发布机制,允许开发者对特定事件做出响应。

相比SpringApplicationRunListenerApplicationListener更通用,不仅限于启动阶段事件,还可监听应用运行过程中的各种事件。

6.2.2 常见启动事件类型

事件类触发时机
ApplicationStartingEventSpring Boot启动最开始时
ApplicationEnvironmentPreparedEvent环境准备完成时
ApplicationPreparedEventApplicationContext创建完毕,准备刷新
ApplicationStartedEventApplicationContext刷新成功
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日志,能有效定位启动耗时阶段。

  • 延迟加载、精准扫描、排除无用自动配置是主要优化手段。

  • 优化启动性能有助于提升开发效率、加快部署速度、降低云环境资源消耗。

http://www.lryc.cn/news/617823.html

相关文章:

  • 18.WEB 服务器
  • Logistic Loss Function|逻辑回归代价函数
  • 人工智能-python-机器学习-逻辑回归与K-Means算法:理论与应用
  • 【电机控制】FOC单电阻电流采样配置
  • DHCP 服务详解与部署
  • React 19 通用 ECharts 组件
  • Redis应⽤-缓存与分布式锁
  • Linux驱动学习day27天(USB驱动理论部分)
  • 修改学生信息管理系统以及查询
  • Ansys Mechanical中的声学分析
  • Nestjs框架: RBAC基于角色的权限控制模型初探
  • java内部类-匿名内部类
  • 适用于高质量核磁共振(NMR)的溶剂推荐
  • Apache ECharts 6 核心技术解密 – Vue3企业级可视化实战指南
  • 每日五个pyecharts可视化图表-line:从入门到精通 (3)
  • 编程技术杂谈4.0
  • SQL复杂查询
  • 论文学习22:UNETR: Transformers for 3D Medical Image Segmentation
  • TCGA数据集下载工具gdc-client下载慢解决方案
  • 掘金数据富矿,永洪科技为山东黄金定制“数智掘金”实战营
  • JavaScript let的使用
  • macos彻底删除vscode
  • 2025年农业工程与环境预防国际会议(ICAEEP 2025)
  • k8s 部署mysql主从集群
  • 用AListLite让安卓手机成为NAS实现文件共享
  • 基于开源模型构建医疗疾病大模型:从理论到实践
  • 2025牛客多校第八场 根号-2进制 个人题解
  • USB 基本描述符
  • TRL - Transformer Reinforcement Learning SFTTrainer 和 SFTConfig
  • AI(2)-神经网络(激活函数)