java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Boot启动流程

Spring Boot启动流程示例详解

作者:探索java

Spring Boot是Spring官方推出的一个快速构建独立运行、生产级别Spring应用的框架,它的核心目标是简化Spring应用的搭建与部署,本文给大家介绍Spring Boot启动流程详解,感兴趣的朋友跟随小编一起看看吧

1. Spring Boot启动流程概述

1.1 Spring Boot的核心特性

Spring Boot是Spring官方推出的一个快速构建独立运行、生产级别Spring应用的框架,它的核心目标是简化Spring应用的搭建与部署
核心特性主要包括:

类比:可以把Spring Boot想象成“开箱即用的开发平台”,你不需要自己组装各种组件,它已经帮你准备好了一整套“工具箱”和“默认配置”。

1.2 启动流程整体架构(文字描述)

Spring Boot启动的核心流程可以抽象成五个阶段:

文字版的启动时序:
SpringApplication实例化 -> 推断Web类型 -> 加载监听器 -> 创建Environment -> 加载配置 -> 创建ApplicationContext -> 自动配置 -> 启动容器 -> 应用就绪

1.3 Spring Boot 2.x 与 3.x 启动机制差异

在Spring Boot 3.x(基于Spring Framework 6)中,启动流程的总体框架与2.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 加载 ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 从 spring.factories 加载 ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推断并设置 main 方法所在类
    this.mainApplicationClass = deduceMainApplicationClass();
}

几个关键点:

示例:自定义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;
}

分步骤解析:

示例:监听启动事件

@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;
}

判断逻辑:

💡 小结

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 作用

小示例:获取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 的配置加载是有优先级顺序的,后加载的会覆盖先加载的。

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

命令行参数覆盖示例

假设 application.properties 中配置:

server.port=8080

运行:

java -jar demo.jar --server.port=9001

最终生效端口是 9001,因为命令行参数优先级最高。

3.3 application.properties 与 application.yml 加载规则

Spring Boot 支持 .properties.yml 两种格式,加载顺序(同一位置)是:

并且,配置文件可以放在多个位置:

优先级顺序(从高到低)

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 会自动绑定配置值。

💡 小结

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);
}

重点

4.2 refreshContext() 核心逻辑解析

refreshContext() 会调用 Spring Framework 的 AbstractApplicationContext.refresh(),这是 IoC 容器生命周期的核心方法。

简化执行步骤(以 Servlet 应用为例):

4.3 BeanFactoryPostProcessor 与 BeanPostProcessor 执行顺序

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

执行顺序

示例:自定义 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,它是应用启动早期的一个轻量级上下文,用于注册启动前所需的组件,典型用途是:

源码片段

DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ApplicationEnvironmentPreparedEvent event =
        new ApplicationEnvironmentPreparedEvent(this, args, environment);
listeners.environmentPrepared(bootstrapContext, environment);

💡 小结

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 {};
}

重点来了:

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)

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

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();
    }
}

解释:

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 {};
}

可以看到:

5.5.2 条件评估流程

条件注解是在 Spring 容器启动阶段,由 ConfigurationClassPostProcessor 解析 @Configuration 类时进行评估的。

流程大致是:

源码片段(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;
}

重点:

5.5.3 常见 Condition 实现类

条件注解对应 Condition 类作用
@ConditionalOnClassOnClassCondition检查类路径是否存在指定类
@ConditionalOnMissingClassOnClassCondition检查类路径是否不存在指定类
@ConditionalOnBeanOnBeanCondition检查容器中是否存在指定 Bean
@ConditionalOnMissingBeanOnBeanCondition检查容器中是否不存在指定 Bean
@ConditionalOnPropertyOnPropertyCondition检查配置文件中属性是否满足
@ConditionalOnWebApplicationOnWebApplicationCondition检查应用是否是 Web 环境
@ConditionalOnExpressionOnExpressionCondition执行 SpEL 表达式并判断结果

5.5.4 执行顺序与调试技巧

命令行启动

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 自动配置的核心原则

5.6.2 自动配置的低优先级实现方式

1.@ConditionalOnMissingBean

示例:

@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
    return new ObjectMapper();
}

意思是:

2.@AutoConfigureBefore/@AutoConfigureAfter
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class MyCustomJsonConfig { ... }

这样你的配置会在 JacksonAutoConfiguration 之前执行。

3.@Primary
4.spring.main.allow-bean-definition-overriding
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

5.6.4 调试 Bean 覆盖问题

Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);

如果报 BeanDefinitionOverrideException,先确认是否是自己命名冲突,必要时允许覆盖。

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

Spring Boot的设计理念之一是高度的可扩展性。在启动过程中,Spring Boot提供了丰富的扩展点,允许开发者在不同阶段插入自定义逻辑,满足多样化的业务需求。这一章将详细介绍Spring Boot启动流程中常用的扩展点,包括SpringApplicationRunListenerApplicationListener以及SpringApplicationBuilder的链式启动配置。

6.1 SpringApplicationRunListener —— 启动事件监听器

6.1.1 作用与触发时机

SpringApplicationRunListener接口用于监听Spring Boot启动流程的各个关键阶段。它的核心目的是在启动流程的不同时间点触发回调,帮助开发者插入自定义操作或日志打印等辅助功能。

Spring Boot启动时会依次调用SpringApplicationRunListener中的如下事件方法:

6.1.2 Spring Boot内部实现及自动加载

Spring Boot通过META-INF/spring.factories文件自动加载所有注册的SpringApplicationRunListener实现。常见的内置实现包括:

自动加载示例(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接口来自定义启动事件监听器。实现步骤:

示例代码:

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的事件发布机制,允许开发者对特定事件做出响应。

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

6.2.2 常见启动事件类型

事件类触发时机
ApplicationStartingEventSpring Boot启动最开始时
ApplicationEnvironmentPreparedEvent环境准备完成时
ApplicationPreparedEventApplicationContext创建完毕,准备刷新
ApplicationStartedEventApplicationContext刷新成功
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与示例

示例:

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启动流程进行高度自定义,满足各种复杂的启动需求,提升应用的健壮性和扩展能力。

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 主要启动性能瓶颈分析

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 总结

到此这篇关于Spring Boot启动流程详解的文章就介绍到这了,更多相关Spring Boot启动流程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文