让 SpringBoot 应用启动更快

2025-06-11

让 SpringBoot 应用启动更快

“春天有多快?”

这是 Spring One Platform 2018 的一个会议。我看了视频,也亲自尝试了一下。所以在这里介绍一下我做了哪些工作以及结果。

如果你还没看过这个会议视频,我推荐你看一下。它非常有趣。

https://springoneplatform.io/2018/sessions/how-fast-is-spring-

今天的源代码

https://github.com/bufferings/spring-boot-startup-mybench

↓我使用了 OpenJDK 11。

❯ java --version
openjdk 11.0.1 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
Enter fullscreen mode Exit fullscreen mode

↓你可以像这样运行所有基准测试。由于要运行所有基准测试,所以需要一些时间。

❯ ./mvnw clean package(cd benchmarks/; java -jar target/benchmarks.jar)
Enter fullscreen mode Exit fullscreen mode

1. FluxBaseline

↓我使用 SpringInitializr 创建了一个 Reactive Web 项目。然后,我编写了一个 WebMVC 风格的小型控制器。

@SpringBootApplication
@RestController
public class DemoApplication {

  @GetMapping("/")
  public String home() {
    return "Hello";
  }

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}
Enter fullscreen mode Exit fullscreen mode

↓Spring Boot 版本为 2.1.0.RELEASE。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
Enter fullscreen mode Exit fullscreen mode

↓启动耗时 2.938 ± 0.287 s/op。

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
Enter fullscreen mode Exit fullscreen mode

现在我得到了检查启动时间的基准。让我们从这里开始吧。

2. WebMVC

↓我好奇 WebMVC 而不是 WebFlux 有什么区别?所以我尝试了一下。也许这只是 Tomcat 和 Netty 的比较?

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case02_Web                               ss   10  3.281 ± 0.342   s/op
Enter fullscreen mode Exit fullscreen mode

WebFlux 更快一点,不是吗?

3. spring-context-indexer

接下来,我尝试了 spring-context-indexer,它似乎创建了组件索引。

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-indexer</artifactId>
            <optional>true</optional>
        </dependency>
Enter fullscreen mode Exit fullscreen mode

↓嗯...慢一点?

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case03_WithContextIndexer                ss   10  3.063 ± 0.102   s/op
Enter fullscreen mode Exit fullscreen mode

↓我检查了一下spring.components,发现它只包含一个组件。原来如此……我应该用一个更大的项目试试效果。

#
#Sun Nov 04 18:42:59 JST 2018
com.example.DemoApplication=org.springframework.stereotype.Component
Enter fullscreen mode Exit fullscreen mode

4.延迟初始化

尝试了 Lazy Init。

@Configuration
public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
      beanFactory.getBeanDefinition(beanName).setLazyInit(true);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

↓结果如下。速度稍微快了一点。

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case04_WithLazyInit                      ss   10  2.844 ± 0.129   s/op
Enter fullscreen mode Exit fullscreen mode

5. NoVerify

运行方式-noverify

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case05_WithNoVerifyOption                ss   10  2.582 ± 0.060   s/op
Enter fullscreen mode Exit fullscreen mode

它变得有点快了。我不知道这意味着什么,所以我需要稍后再检查一下。

6. 分层停止

运行方式-XX:TieredStopAtLevel=1

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case06_WithTieredStopAtLevel1Option      ss   10  1.980 ± 0.037   s/op
Enter fullscreen mode Exit fullscreen mode

呃,快多了!不到两秒就搞定了。不过我也不知道这个标志。我以后再看看。

7.明确指定SpringConfigLocation

运行方式-Dspring.config.location=classpath:/application.properties

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case07_WithSpringConfigLocationOption    ss   10  3.026 ± 0.139   s/op
Enter fullscreen mode Exit fullscreen mode

嗯,变慢了。

8.关闭JMX

运行方式-Dspring.jmx.enabled=false

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case08_WithJmxDisabledOption             ss   10  2.877 ± 0.097   s/op
Enter fullscreen mode Exit fullscreen mode

它变得快了一点。

9. 排除 Logback

从这里开始,我尝试排除一些库。首先,排除 Logback:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
        </dependency>
Enter fullscreen mode Exit fullscreen mode

结果如下:

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case09_WithoutLogback                    ss   10  2.904 ± 0.096   s/op
Enter fullscreen mode Exit fullscreen mode

嗯……稍微好一点?

10. 排除杰克逊

接下来是杰克逊

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-json</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
Enter fullscreen mode Exit fullscreen mode

结果:

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case10_WithoutJackson                    ss   10  2.789 ± 0.093   s/op
Enter fullscreen mode Exit fullscreen mode

它变得快了一点。

11. 排除 HibernateValidator

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>hibernate-validator</artifactId>
                    <groupId>org.hibernate.validator</groupId>
                </exclusion>
            </exclusions>
        </dependency>
Enter fullscreen mode Exit fullscreen mode

结果如下:

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case11_WithoutHibernateValidator         ss   10  2.857 ± 0.084   s/op
Enter fullscreen mode Exit fullscreen mode

也略有改善。

图书馆排除的时代就此结束。

12. AppCDS

AppCDS(应用程序类数据共享)最初作为一项商业功能包含在 Oracle JDK 中。但它从 OpenJDK 10 开始可用。

AppCDS 似乎将信息转储到共享档案中,因此启动时间变短。

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case12_WithAppCds                        ss   10  2.957 ± 0.079   s/op
Enter fullscreen mode Exit fullscreen mode

嗯... 没有更快... 然后我查看了有关CDS的文章,找到了原因。

使用 SpringBoot FatJAR,这些库超出了 CDS 的范围。

13. Flux 与 Thin Launcher

呃,抱歉,基准测试名称“Exploded”错了。我曾经尝试过解压 FatJAR,但解压后的 JAR 无法使用 CDS。所以我改用 Thin Launcher。请将基准测试名称“Exploded”改为“Thin Launcher”。

在使用CDS之前,我想检查一下用Thin Launcher打包的JAR文件的速度。

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.boot.experimental</groupId>
                        <artifactId>spring-boot-thin-layout</artifactId>
                        <version>1.0.15.RELEASE</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
Enter fullscreen mode Exit fullscreen mode

虽然我使用了Thin Launcher来打包应用程序,但是我并没有使用Thin Launcher的启动类,而是指定了Main类,以使启动时间尽可能快。

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case13_Exploded                          ss   10  2.476 ± 0.091   s/op
Enter fullscreen mode Exit fullscreen mode

嗯,是不是快了一点?

14. Thin Launcher + CDS

现在我想将 AppCDS 应用于它。

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case14_ExplodedWithAppCds                ss   10  1.535 ± 0.036   s/op
Enter fullscreen mode Exit fullscreen mode

哇!速度快多了!

15. 全部适用

最后,我应用了所有方法。

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case15_AllApplied                        ss   10  0.801 ± 0.037   s/op
Enter fullscreen mode Exit fullscreen mode

不到1秒!(∩´∀`)∩yay

再一步

在 Dave 的讲座中,他提到了“函数式 Bean 定义”,并尝试使用 Spring 进行改进(无需 SpringBoot),应用程序速度明显加快。我需要学习更多才能理解这些改进。

结果列表

Benchmark                                          Mode  Cnt  Score   Error  Units
MyBenchmark.case01_FluxBaseline                      ss   10  2.938 ± 0.287   s/op
MyBenchmark.case02_Web                               ss   10  3.281 ± 0.342   s/op
MyBenchmark.case03_WithContextIndexer                ss   10  3.063 ± 0.102   s/op
MyBenchmark.case04_WithLazyInit                      ss   10  2.844 ± 0.129   s/op
MyBenchmark.case05_WithNoVerifyOption                ss   10  2.582 ± 0.060   s/op
MyBenchmark.case06_WithTieredStopAtLevel1Option      ss   10  1.980 ± 0.037   s/op
MyBenchmark.case07_WithSpringConfigLocationOption    ss   10  3.026 ± 0.139   s/op
MyBenchmark.case08_WithJmxDisabledOption             ss   10  2.877 ± 0.097   s/op
MyBenchmark.case09_WithoutLogback                    ss   10  2.904 ± 0.096   s/op
MyBenchmark.case10_WithoutJackson                    ss   10  2.789 ± 0.093   s/op
MyBenchmark.case11_WithoutHibernateValidator         ss   10  2.857 ± 0.084   s/op
MyBenchmark.case12_WithAppCds                        ss   10  2.957 ± 0.079   s/op
MyBenchmark.case13_Exploded                          ss   10  2.476 ± 0.091   s/op
MyBenchmark.case14_ExplodedWithAppCds                ss   10  1.535 ± 0.036   s/op
MyBenchmark.case15_AllApplied                        ss   10  0.801 ± 0.037   s/op
Enter fullscreen mode Exit fullscreen mode

真的很有趣。谢谢!

鏂囩珷鏉ユ簮锛�https://dev.to/bufferings/lets-make-springboot-app-start-faster-k9m
PREV
Bun 与 Node.js:你需要知道的一切
NEXT
2021 年值得添加到你的技术栈的 5 个最佳开源表单构建器