20

GraalVM 系统显然无法将 Spring 应用程序编译为原生镜像。

我们是否可以编译 Spring 应用程序的一个子集——比如说,作为一个单独的库——然后将其与使用通常的 javac 编译器编译的其余部分一起使用?

或者,如果我们从应用程序中遗漏了一些 Spring 特性?

还有其他可能吗?

4

5 回答 5

50

我们(Spring 团队)刚刚发布了一篇非常详细的博客文章和一段视频,发布了Spring Native测试版,为这个问题提供了一个可能更新的答案。

mvn spring-boot:build-image它允许您通过 Spring Boot或gradle bootBuildImage命令使用 GraalVM 本机映像编译器将 Spring 应用程序编译为本机可执行文件,或者仅native-image通过native-image-maven-plugin.

一个 Spring Boot 应用程序从 38 毫秒开始编译为本机镜像

使用它的最有用的链接是start.spring.io,它现在提供 Spring Native 支持和参考文档的入门部分。

在 start.spring.io 上提供 Spring Native 支持

确保正确配置Spring AOT Maven 和 Gradle 插件,这是为您的 Spring 应用程序获得适当本机支持所必需的。

享受!

于 2021-03-12T07:57:27.937 回答
44

这个问题的开场白有点含糊,因此很难正确解决。

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 问题跟踪票,人们可以按照它来查看开发情况。

于 2018-07-03T12:16:54.930 回答
5

正如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 seconds0.08 seconds

简而言之,如果您想在生产中使用该功能,您必须等待 2020 年末的最终 Spring 5.3 版本以及基于它的 Spring Boot 版本。如果您想开始实验性地使用该功能,您可以立即开始

asciiccast

====== 2020 年 6 月 10 日更新到 spring-graalvm-native 0.7.0 版本。=======

以下是基本步骤(2020 年 6 月)源自spring-projects-experimental 项目 spring-graalvm-native的最新文档和我最近写的这篇博文(步骤 7 和 8 可以使用compile.shbash 脚本或使用的帮助native-image-maven-plugin- 这两种选择都在下面解释):

  1. 编写您的 Spring Boot 应用程序(例如从https://start.spring.io 重新开始或使用现有应用程序)
  2. 在 pom.xml 中升级到最新版本的 Spring Boot 2.3.x(不要从 2.2 或更低版本开始!)
  3. 在 pom.xml 中设置 start-class 元素

该命令稍后需要带注释的类native-image的完全限定类名。在你的属性@SpringBootApplication中定义它,如下所示:pom.xml

<properties>
        ...
        <start-class>io.jonashackt.springbootgraal.SpringBootHelloApplication</start-class>
</properties>
  1. 禁用 GCLIB 代理

由于 GraalVM 不支持 GCLIB 代理,因此 Spring Boot 需要使用 JDK 代理。因此使用proxyBeanMethods = false@SpringBootApplication 类的属性:

@SpringBootApplication(proxyBeanMethods = false)
public class SpringBootHelloApplication {
    ...
}
  1. 安装 GraalVM 20.1.0 和 GraalVM Native Image 命令

最简单的方法是使用 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。有关详细信息,请参阅此博客文章

  1. 将 Annotation 类路径扫描从运行时重新定位到构建时间 & 6. 检测自动配置

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>
  1. 准备执行本机映像命令

本质上,我们需要为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
  1. 制作 native-image 命令并运行编译

现在我们已经准备好制作并最终运行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 如下:

  1. 添加 spring-context-indexer 依赖

由于 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文件,然后由本机图像编译过程拾取。

  1. 添加 native-image-maven-plugin 作为 Maven 构建配置文件

为了让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-05-08T13:20:13.440 回答
2

截至 2020 年 5 月,Spring 发布了 Spring Graalvm Native。 Spring Graalvm 原生

于 2020-05-29T07:13:02.883 回答
0

目前可以使用QuarkusMicronautVert.X等具有 Spring 兼容特性或可与 Spring 一起使用的替代品。它们都可以编译成小的本机代码二进制可执行文件,立即启动并且内存占用很小。

这些新框架通过在构建时解释注释和其他规范来避免 GraalVM 的限制。通过这种方式,他们避免了 GraalVM AOT 编译器native-image无法支持的 Java 运行时特性。

于 2020-01-28T06:14:47.813 回答