SpringBoot3.x原生镜像-Native Image实践
前提
之前曾经写过一篇《SpringBoot3.x 原生镜像-Native Image 尝鲜》,当时SpringBoot处于3.0.0-M5版本,功能尚未稳定。这次会基于SpringBoot当前最新的稳定版本3.1.2详细分析Native Image的实践过程。系统或者软件版本清单如下:
| 组件 | 版本 | 备注 |
|---|---|---|
macOS Ventura |
13.4.1(c) |
ARM架构 |
sdkman |
5.18.2 |
JDK和各类SDK包管理工具 |
Liberica Native Image Kit |
23.0.1.r17-nik |
可以构建Native Image的JDK |
SpringBoot |
3.1.2 |
使用当前(2023-08-20)最新发布版 |
Maven |
3.9.0 |
- |
安装 sdkman
sdkman是一个轻量级、支持多平台的开源开发工具管理器,可以通过它安装任意主流发行版本(例如OpenJDK、Kona、GraalVM等等)的任意版本的JDK。通过下面的命令可以轻易安装sdkman:
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk version

可以通过sdk list java查看支持的JDK发行版本:

通过shell命令sdk install java $Identifier就可以安装对应的JDK发行版。例如可以这样安装GraalVM-ce-17:
sdk install java 17.0.8-graalce
通过shell命令sdk uninstall java $Identifier可以卸载对应的JDK发行版。如果安装了多个版本或者多个发行版的JDK,可以通过shell命令sdk default java $Identifier去指定默认使用的JDK版本,例如:
sdk default java 17.0.8-graalce
可以通过shell命令sdk current或者sdk current java查看当前正在使用的SDK或者JDK版本。

安装 Liberica NIK
Liberica Native Image Kit是bellsoft出品的旨在创建高性能原生二进制(Native Binaries)基于JVM编写的应用的工具包,简称为Liberica NIK。Liberica NIK本质就是把OpenJDK和多种其他工具包一起封装起来的JDK发行版,在Native Image功能应用过程,可以简单把它视为OpenJDK + GraalVM的结合体。可以通过sdk list java查看相应的JDK版本:

这里选择JDK-17的版本进行安装:
sdk install java 23.0.1.r17-nik
# 这里最好把此JDK设置为当前系统的默认JDK,否则后面编译镜像时候会提示找不到GraalVM
sdk default java 23.0.1.r17-nik
安装完成后,通过java -version验证一下:

编写 SpringBoot 应用
基于Maven新建一个SpringBoot应用,这里已经整理好了一份POM文件,实践过程可以直接用,如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.vlts</groupId>
<artifactId>spring-boot-native-image-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.2</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.version>3.11.0</maven.compiler.version>
<maven.install.version>3.1.1</maven.install.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat.experimental</groupId>
<artifactId>tomcat-embed-programmatic</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>${maven.install.version}</version>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>cn.vlts.NativeImageApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
这里把Maven的所有插件都提升到当前(2023-08-20前后)最新版本,原生镜像打包的关键插件是native-maven-plugin,此插件是跟随spring-boot-starter-parent进行版本管理,这里无须指定插件的版本。另外,tomcat-embed-programmatic是一个实验性依赖,可以降低嵌入式Tomcat的内存使用,在生产中应用时候可以暂不启用此特性。接着编写启动类cn.vlts.NativeImageApplication:
@SpringBootApplication
@RestController
public class NativeImageApplication {
public static void main(String[] args) {
SpringApplication.run(NativeImageApplication.class, args);
}
@RequestMapping(path = "/")
public ResponseEntity<String> index() {
return ResponseEntity.ok("index");
}
}
构建、测试与发布
三个操作的Maven命令分别是:
- 构建:
mvn -Pnative native:compile - 测试:
mvn -PnativeTest test - 发布:
mvn -Pnative spring-boot:build-image,注意此命令会打包镜像并且发布到Docker的官方仓库中
虽然 native:compile 命令表面意义是编译,但是实际上它就是构建原生镜像的命令
执行构建流程:
mvn -Pnative native:compile -Dmaven.test.skip=true
构建结果如下:

其中这个不带.jar后缀的就是最终的原生镜像,并且Native Image是不支持跨平台的,它只能在ARM架构的macOS中运行(受限于笔者的编译环境)。可以发现它(见上图中的target/spring-boot-native-image-demo,它是一个二进制执行文件)的体积比executable jar大好几倍。参照SpringBoot的官方文档,经过AOT编译的SpringBoot应用会生成下面的文件:
Java源代码- 字节码(例如动态代理编译后的产物等)
GraalVM识别的提示文件:- 资源提示文件(
resource-config.json) - 反射提示文件(
reflect-config.json) - 序列化提示文件(
serialization-config.json) Java(动态)代理提示文件(proxy-config.json)JNI提示文件(jni-config.json)
- 资源提示文件(
这里的输出非执行包产物基本都在target/spring-aot目录下,其他非Spring或者项目源代码相关的产物输出到graalvm-reachability-metadata目录中。最后可以验证一下产出的Native Image:

可以看到启动速度达到惊人的毫秒级别,如果应用在生产中应该可以全天候近乎无损发布。当然,理论上Native Image性能也会大幅度提升,但是限于篇幅这里暂时不进行性能测试。
小结
鉴于SpringBoot3.x的正式版已经推出一段时间,从文档上看,Native Image使用的技术已经相对成熟,可以放心用于生产环境。当然,Native Image目前还存在一些局限性会让一些组件完全无法使用或者部分功能受限(参考Spring Boot with GraalVM),希望这些问题或者局限性有一天能够突破让所有JVM应用迎来一次性能飞跃。
(本文完 c-2-d e-a-20230820)
SpringBoot3.x原生镜像-Native Image实践的更多相关文章
- SpringBoot3.x原生镜像-Native Image尝鲜
前提 Spring团队致力于为Spring应用程序提供原生映像支持已经有一段时间了.在SpringBoo2.x的Spring Native实验项目中酝酿了3年多之后,随着Spring Framewor ...
- 8.云原生之Docker容器镜像构建最佳实践浅析
转载自:https://www.bilibili.com/read/cv15220861/?from=readlist 本章目录 0x02 Docker 镜像构建最佳实践浅析 1.Dockerfile ...
- 未来已来:云原生 Cloud Native
作者:天知,原文链接 前言 自 2013 年容器(虚拟)技术(Docker)成熟后,后端的架构方式进入快速迭代的阶段,出现了很多新兴概念: 微服务 k8s Serverless IaaS:基础设施服务 ...
- 制作 Python Docker 镜像的最佳实践
概述 ️Reference: 制作容器镜像的最佳实践 这篇文章是关于制作 Python Docker 容器镜像的最佳实践.(2022 年 12 月更新) 最佳实践的目的一方面是为了减小镜像体积,提升 ...
- 微信原生支付 Native扫码支付( V3.3.7 版本)
原文:微信原生支付 Native扫码支付( V3.3.7 版本) [尊重别人的劳动成果,转载请注明出处:一缕晨光工作室,www.wispdawn.com] 前言 辛苦研究三天,遇到各种困难,最终还是克 ...
- 深度解读阿里巴巴云原生镜像分发系统 Dragonfly
Dragonfly 是一个由阿里巴巴开源的云原生镜像分发系统,主要解决以 Kubernetes 为核心的分布式应用编排系统的镜像分发难题.随着企业数字化大潮的席卷,行业应用纷纷朝微服务架构演进,并通过 ...
- 5大最新云原生镜像构建工具全解析,3个来自Google,你了解几个?
1云原生大背景下的镜像构建在分享开始,我想先跟大家简单聊一下云原生,可能不会详细展开,而是带领大家了解一下云原生对镜像构建方面的影响.第一,在接触云原生相关的技术时,无论是要解决开发.测试环境的问题, ...
- 关于OAuth2.0 Authorization Code + PKCE flow在原生客户端(Native App)下集成的一点思考
写在前面 前几天看了园友的一篇文章被广泛使用的OAuth2.0的密码模式已经废了,放弃吧 被再次提起: Implicit Flow Password Grant,均已被标记为Legacy,且OAuth ...
- 精彩分享 | 欢乐游戏 Istio 云原生服务网格三年实践思考
作者 吴连火,腾讯游戏专家开发工程师,负责欢乐游戏大规模分布式服务器架构.有十余年微服务架构经验,擅长分布式系统领域,有丰富的高性能高可用实践经验,目前正带领团队完成云原生技术栈的全面转型. 导语 欢 ...
- react native 入门实践
上周末开始接触react native,版本为0.37,边学边看写了个demo,语法使用es6/7和jsx.准备分享一下这个过程.之前没有native开发和react的使用经验,不对之处烦请指出.希望 ...
随机推荐
- Netty自定义协议要素
魔数:用来判断是否是无效数据包 协议版本号:可以支持协议的升级 序列化算法:消息正文使用哪种序列化方式,可以扩展.例如:protobuf,json,hessian等 指令类型:跟业务相关,例如:登录, ...
- Python基础 - 逻辑运算符
Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20: 运算符 逻辑表达式 描述 实例 and x and y 布尔"与" - 如果 x 为 False,x a ...
- WPF 入门笔记 - 03 - 样式基础及控件模板
原学习路线是按照圣殿骑士的<WPF基础到企业应用系列>的路线走的,但是布局之后直接依赖属性学起来有些僵硬,不太好理解,尝试了文章的前部分内容后放弃,调整为本篇博文内容.笔记路线将按照痕迹g ...
- Simple Date Format类到底为啥不是线程安全的?
摘要:我们就一起看下在高并发下Simple Date Format类为何会出现安全问题,以及如何解决Simple Date Format类的安全问题. 本文分享自华为云社区<[高并发]Simpl ...
- STL-queue(ACM)
重构函数(默认) queue<int> q; 基本操作 q.front(); // 队列最前面的元素q.back(); // 队列最后面的元素q.size(); // 返回队列长度q.em ...
- json数据的解析
php声明没有键的数组,用json_encode输出: $array=array("1","2","3"); echo json_encod ...
- 磐舟磐基平台:基于KubeEdge的落地实践
摘要:实现统一管理.简化多集群的运维系统.减少运营成本:同时也成功将前面提到的500台鲲鹏服务器以及它上面的BC Linux for Euler集群纳入磐基PaaS平台的大家庭之中,运维效率大幅增加. ...
- 一文解开主流开源变更数据捕获技术之Flink CDC的入门使用
@ 目录 概述 定义 什么是CDC? CDC的分类 特性 应用场景 支持数据源 实战 Flink DataStream方式代码示例 FlinkSQL方式代码示例 概述 定义 flink-cdc-con ...
- 通用权限系统-Dozer对象转换
Dozer对象转换 介绍 Dozer 是一个 Java Bean 到 Java Bean 的映射器,它可以递归地将数据从一个对象复制到另一个.通常情况下,这些Java Beans将是不同的复杂类型. ...
- 落地微服务架构v2.0
网关+服务治理 将服务注册到Consul中,需要首先系统该插件. Ocelot:网关组件,腾讯和微软都在使用. 可以使用dotnet run命令启动.NET Core项目,启动时可以在命令时传递监听的 ...