ncy>
<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