GraalVM 系统显然无法将 Spring 应用程序编译为原生镜像。
我们是否可以编译 Spring 应用程序的一个子集——比如说,作为一个单独的库——然后将其与使用通常的 javac 编译器编译的其余部分一起使用?
或者,如果我们从应用程序中遗漏了一些 Spring 特性?
还有其他可能吗?
我们(Spring 团队)刚刚发布了一篇非常详细的博客文章和一段视频,发布了Spring Native测试版,为这个问题提供了一个可能更新的答案。
mvn spring-boot:build-image
它允许您通过 Spring Boot或gradle bootBuildImage
命令使用 GraalVM 本机映像编译器将 Spring 应用程序编译为本机可执行文件,或者仅native-image
通过native-image-maven-plugin
.
使用它的最有用的链接是start.spring.io,它现在提供 Spring Native 支持和参考文档的入门部分。
确保正确配置Spring AOT Maven 和 Gradle 插件,这是为您的 Spring 应用程序获得适当本机支持所必需的。
享受!
这个问题的开场白有点含糊,因此很难正确解决。
GraalVM 绝对可以编译 Spring 应用程序。GraalVM 的分发与普通的 JDK 非常相似,它包括一个javac
实用程序,一个java
实用程序,可以添加到路径中并正常使用。您可以将$JAVA_HOME
环境变量设置为指向解压 GraalVM 发行版的目录,添加$JAVA_HOME/bin
到路径中,并按照您通常构建它们的方式构建 Spring 应用程序,使用 Maven 或 Gradle 或任何其他构建工具。
GraalVM 还可以运行 Spring 应用程序,由它自己和其他 JVM 编译。如果您好奇,这里有一个 Spring 应用程序示例,它不仅在 GraalVM 上运行,而且还使用 R 来可视化数据图,使用 GraalVM 多语言功能。
现在,我猜你的意思是 GraalVM 能够创建一些 Java 程序的可执行本机映像。
更新:2021 年 3 月 13 日
Spring Native 项目处于测试阶段,您可以使用它。Sébastien Deleuze 接受的答案是一个很好的调查起点:https ://stackoverflow.com/a/66596191/1087978
更新:2019 年 11 月 17 日
一些 Spring 应用程序作为 GraalVM 原生镜像工作。Pivotal 和 GraalVM 团队正在积极努力改善支持。这是来自 Devoxx Belgium 2019 的 Sébastien Deleuze 的一次关于 Spring 应用程序和 GraalVM 原生图像状态的会议,他展示了一个小型 hello world Spring 应用程序作为原生镜像工作,以及使用 JPA 和内存数据库作为原生工作的 vanilla Spring Petclinic 演示图片:https ://www.youtube.com/watch?v=3eoAxphAUIg
您可以按照此处的说明进行操作:https ://github.com/spring-projects-experimental/spring-graalvm-native来构建或调查示例。
请注意,该项目是实验性的,因为它在其 README 中也有说明。
对本机映像的支持还没有优化,它会变得更好,如果我尝试spring-petclinic-jpa
这个存储库中的示例,它可以在我不强大的 macbook 上在大约 200 毫秒内启动:
14:13:11.990 [main] INFO o.s.s.petclinic.PetClinicApplication -
Started PetClinicApplication in 0.171 seconds (JVM running for 0.173)
上一次更新:2019 年 5 月 17 日
这是GraalVM 本机图像支持的 spring-framework wiki 页面。
由 Andy Clement 创建的spring-graalvm-native实验项目展示了如何将 Spring Boot 应用程序作为 GraalVM 原生镜像开箱即用地运行。它可以用作潜在的即将到来的官方支持的基础。
总而言之,您可以尝试一下,但事情可能无法完全按预期进行。
上一个答案如下:
有一个spring-fu项目,一个基于功能配置的实验性 Kotlin 微框架,旨在测试未来 Spring Boot 版本的新想法,目前正在试验能够通过 GraalVM 编译为本机图像。
与此同时,GraalVM 团队正在研究可以做些什么来简化将 Spring 应用程序编译为原生镜像并支持比目前更多的 Spring 应用程序。一些限制仍然存在,因此您将始终无法构建一个不能作为 GraalVM 本地映像工作的 Spring 应用程序,但也许您将能够构建也可以工作的 Spring 应用程序。
这些变化的确切路线图目前尚不清楚。
这是一个SpringFramework 问题跟踪票,人们可以按照它来查看开发情况。
正如Oleg Šelajev所说,使用GraalVM Native Image(这是GraalVM的一个子项目)对 Spring Boot 应用程序进行本地编译是可能的,但目前有限制,并计划在 2020 年秋季与 Spring Framework 的 5.3 版本一起发布。使用 Native Image,您可以在内存占用和启动时间减少方面获得与使用 Quarkus.io、Micronaut 等类似的优势。我能够在示例项目实施中减少内存占用和500MB
启动30MB
时间一个反应式 Spring Boot Web 应用程序。1.5 seconds
0.08 seconds
简而言之,如果您想在生产中使用该功能,您必须等待 2020 年末的最终 Spring 5.3 版本以及基于它的 Spring Boot 版本。如果您想开始实验性地使用该功能,您可以立即开始。
====== 2020 年 6 月 10 日更新到 spring-graalvm-native 0.7.0 版本。=======
以下是基本步骤(2020 年 6 月),源自spring-projects-experimental 项目 spring-graalvm-native的最新文档和我最近写的这篇博文(步骤 7 和 8 可以使用compile.sh
bash 脚本或使用的帮助native-image-maven-plugin
- 这两种选择都在下面解释):
该命令稍后需要带注释的类native-image
的完全限定类名。在你的属性@SpringBootApplication
中定义它,如下所示:pom.xml
<properties>
...
<start-class>io.jonashackt.springbootgraal.SpringBootHelloApplication</start-class>
</properties>
由于 GraalVM 不支持 GCLIB 代理,因此 Spring Boot 需要使用 JDK 代理。因此使用proxyBeanMethods = false
@SpringBootApplication 类的属性:
@SpringBootApplication(proxyBeanMethods = false)
public class SpringBootHelloApplication {
...
}
最简单的方法是使用 SDKMAN:
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 20.1.0.r11-grl
gu install native-image
通过键入java -version
(应列出 GraalVM)和. 检查两者是否正常工作native-image --version
。有关详细信息,请参阅此博客文章。
native-image
这两个步骤都由稍后与命令一起使用的 Spring Graal @AutomaticFeature 为您完成。由于 @AutomaticFeature已经在 Spring Milestones 存储库中发布,我们可以简单地添加一个依赖项到我们的pom.xml
(不要忘记现在也添加 Spring Milestones 存储库,因为它现在不通过 Maven Central 提供):
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graalvm-native</artifactId>
<version>0.7.1</version>
</dependency>
...
<dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
本质上,我们需要为native-image
命令准备配置变量,然后构建应用程序,展开 Spring Boot fat JAR 并配置类路径。我创建了一个使用 bash 执行必要步骤的compile.sh:
#!/usr/bin/env bash
echo "[-->] Detect artifactId from pom.xml"
ARTIFACT=$(mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.artifactId}' \
--non-recursive \
exec:exec);
echo "artifactId is '$ARTIFACT'"
echo "[-->] Detect artifact version from pom.xml"
VERSION=$(mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.version}' \
--non-recursive \
exec:exec);
echo "artifact version is '$VERSION'"
echo "[-->] Detect Spring Boot Main class ('start-class') from pom.xml"
MAINCLASS=$(mvn -q \
-Dexec.executable=echo \
-Dexec.args='${start-class}' \
--non-recursive \
exec:exec);
echo "Spring Boot Main class ('start-class') is '$MAINCLASS'"
echo "[-->] Cleaning target directory & creating new one"
rm -rf target
mkdir -p target/native-image
echo "[-->] Build Spring Boot App with mvn package"
mvn -DskipTests package
echo "[-->] Expanding the Spring Boot fat jar"
JAR="$ARTIFACT-$VERSION.jar"
cd target/native-image
jar -xvf ../$JAR >/dev/null 2>&1
cp -R META-INF BOOT-INF/classes
echo "[-->] Set the classpath to the contents of the fat jar & add the Spring Graal AutomaticFeature to the classpath"
LIBPATH=`find BOOT-INF/lib | tr '\n' ':'`
CP=BOOT-INF/classes:$LIBPATH
现在我们已经准备好制作并最终运行native-image
命令。这是一个示例,它基于上述示例项目实现 Reactive Spring Boot Web 应用程序。这个现在很棘手,并且取决于您要编译为 GraalVM Native Image 的那种 Spring Boot 应用程序!因此最好的方法是从spring-graal-native 项目的示例项目中获得一些灵感:
GRAALVM_VERSION=`native-image --version`
echo "[-->] Compiling Spring Boot App '$ARTIFACT' with $GRAALVM_VERSION"
time native-image \
-H:+TraceClassInitialization \
-H:Name=$ARTIFACT \
-H:+ReportExceptionStackTraces \
-Dspring.native.remove-unused-autoconfig=true \
-Dspring.native.remove-yaml-support=true \
-cp $CP $MAINCLASS;
最后通过执行 bash 脚本./compile.sh
并喝杯咖啡!这需要一些时间,具体取决于您的硬件!在我迟到的 MBP 2017 上,示例项目大约需要 3-4 分钟。如果一切顺利,您将在/target/native-image/spring-boot-graal
. 只需运行它:
./target/native-image/spring-boot-graal
===============================
7 和 8 的替代方案:native-image-maven-plugin
除了 bash 脚本(以及描述的步骤 7 和 8)之外,还有native-image-maven-plugin。但请仅在您确定如何配置native-image
命令的情况下使用它——因为它的执行现在非常麻烦(我相信到 2020 年底会有很多改进)。如果要使用该插件,步骤代替 7 & 8 如下:
由于 Spring @AutomaticFeature 在使用时不会自动探索所需的 Spring 组件native-image-maven-plugin
(这是一个错误吗?),我们需要明确添加spring-context-indexer
来完成这项工作:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
</dependency>
它创建一个target/classes/META_INF/spring.components
文件,然后由本机图像编译过程拾取。
为了让native-image-maven-plugin正常工作,最好为本地映像编译创建一个新的 Maven 配置文件(请参阅此 pom.xml 以获取完整工作示例):
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>20.1.0</version>
<configuration>
<buildArgs>-H:+TraceClassInitialization -H:+ReportExceptionStackTraces -Dspring.native.remove-unused-autoconfig=true -Dspring.native.remove-yaml-support=true</buildArgs>
<imageName>${project.artifactId}</imageName>
</configuration>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
我们需要spring-boot-maven-plugin
再次添加,因为它为原生图像插件准备了必要的配置。
关键部分是buildArgs
需要继承脚本中native-image
命令参数的标签。compile.sh
与那个相比,我们可以省略-cp $CP $MAINCLASS
参数,因为插件识别包含主类本身的类路径(仅后者,如果start-class
设置了步骤 3 中的标记)。Using<imageName>${project.artifactId}</imageName>
是一个好主意,以便将我们artifactId
的结果用于生成的可执行映像名称。
现在只需通过以下方式执行 Maven 配置文件:
mvn -Pnative clean package
如果编译成功,请使用以下命令启动您的本机 Spring Boot 应用程序:
./target/spring-boot-graal
===============================
如果你想在 TravisCI 这样的 CI 服务器上运行本机映像编译或使用 Docker 进行编译,我可以推荐这个答案和这篇博文。也可以在 TravisCI 上查看完整的编译过程。
截至 2020 年 5 月,Spring 发布了 Spring Graalvm Native。 Spring Graalvm 原生