将 Spring 个应用程序编译为 GraalVM 本机映像

Compiling Spring Applications to GraalVM Native Images

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

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

或者如果我们从我们的应用程序中遗漏一些 Spring 功能?

还有其他可能性吗?

这个问题的开头语有点含糊,所以很难正确地解决它。

GraalVM 绝对可以编译 Spring 应用程序。 GraalVM distribution 与普通的 JDK 非常相似,它包括一个 javac 实用程序,一个 java 实用程序,可以将其添加到路径并正常使用。 您可以将 $JAVA_HOME 环境变量设置为指向您解压 GraalVM 发行版的目录,将 $JAVA_HOME/bin 添加到路径,并按照您通常构建它们的方式构建 Spring 应用程序,使用 Maven或 Gradle,或任何其他构建工具。

GraalVM 还可以 运行 Spring 应用程序,由其自身和其他 JVM 编译。如果您很好奇,这里 an example of a Spring application 不仅 运行 在 GraalVM 上,而且还使用 R 可视化数据图,使用 GraalVM 多语言功能。

现在,我猜你的意思是 GraalVM 能够 create executable native images of some Java programs

更新:2021 年 3 月 13 日

Spring 本机项目处于测试阶段,您可以使用它。 Sébastien Deleuze 接受的答案是一个很好的调查起点:https://whosebug.com/a/66596191/1087978

更新:2019 年 11 月 17 日

一些 Spring 应用程序作为 GraalVM 本机图像工作。 Pivotal 和 GraalVM 团队积极致力于改善支持。这是来自 Devoxx Belgium 2019 的塞巴斯蒂安·德勒兹 (Sébastien Deleuze) 关于 Spring 应用程序和 GraalVM 本机图像的状态的 session,他展示了一个小的 hello world Spring 应用程序作为本机图像和 vanilla Spring Petclinic 演示使用 JPA 和 in-memory 数据库作为本机图像工作:https://www.youtube.com/watch?v=3eoAxphAUIg

您可以按照此处的说明进行操作:https://github.com/spring-projects-experimental/spring-graalvm-native 构建或研究样本。

注意这个项目是实验性的,因为它也在其自述文件中注明。

对本机图像的支持还没有优化,它会变得更好,目前如果我尝试这个存储库中的 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 日

这是 spring-framework 的 wiki page for GraalVM native image support

The spring-graalvm-native experimental project, created by Andy Clement, shows how it is possible to run a Spring Boot application out of the box as a GraalVM native image. It could be used as a basis for a potential upcoming official support.

总而言之,您可以尝试一下,但事情可能无法完全按预期进行。

之前的回答如下:

有一个 spring-fu project, an experimental Kotlin micro-framework based on functional configuration intended to test new ideas for future Spring Boot releases, which is currently experimenting 可以通过 GraalVM 编译为原生图像。

与此同时,GraalVM 团队正在研究如何简化将 Spring 应用程序编译为本机映像并支持比目前更多的 Spring 应用程序。一些限制将仍然存在,因此您将始终无法构建一个不能用作 GraalVM 本机映像的 Spring 应用程序,但也许您将能够构建 Spring 个应用程序也会工作。
这些变化的确切路线图目前尚不清楚。

这里有一个 SpringFramework issue tracker ticket 可以关注它的发展。

目前可以使用 Quarkus, Micronaut and Vert.X 等具有 Spring 兼容功能或可与 Spring 一起使用的替代方案。它们都可编译为小型本机代码二进制可执行文件,可立即启动且内存占用很小。

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

Oleg Šelajev already stated, native compilation of Spring Boot apps with GraalVM Native Image (which is a sub project of GraalVM) is possible with limitations right now and is planned to be released with the Spring Framework’s 5.3 release in autumn 2020. With Native Image you're able to achieve similar advantages in memory footprint and startup time reduction as you get with using Quarkus.io, Micronaut etc. I was able to reduce memory footprint from around 500MB to 30MB and startup time from 1.5 seconds to 0.08 seconds in an example project implementing a Reactive Spring Boot Web app.

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

====== 已更新为 spring-graalvm-native 0.7.0 release at June 10, 2020。 =======

这是基本步骤(2020 年 6 月)derived from the latest docs of the spring-projects-experimental project spring-graalvm-native and this blog post 我最近写道(第 7 步和第 8 步可以通过 compile.sh bash 脚本或在 native-image-maven-plugin 的帮助下 - 两种选择都在下面解释):

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

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

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

由于 GraalVM 不支持 GCLIB 代理,Spring 启动需要 use JDK proxies instead。因此使用你的@SpringBootApplicationclass:

proxyBeanMethods = false属性
@SpringBootApplication(proxyBeanMethods = false)
public class SpringBootHelloApplication {
    ...
}
  1. 安装 GraalVM 20.1.0 和 GraalVM 本机映像命令

最简单的方法是使用 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 检查两者是否正常工作。参见 this blog post for more details

  1. 重定位注解class路径扫描从运行时间到构建时间 & 6.检测自动配置

这两个步骤都由稍后与 native-image 命令一起使用的 Spring Graal @AutomaticFeature 为您完成。作为@AutomaticFeature was already released on Spring Milestones repository,我们可以简单地向我们的pom.xml 添加一个依赖项(不要忘记现在还添加Spring 里程碑存储库,因为这不是通过 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 并配置 class 路径。我创建了一个 compile.sh 来执行必要的步骤 bash:

#!/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 命令。这是一个示例,它基于提到的 example project implementing a Reactive Spring Boot Web app. This one is tricky right now and is dependend on the kind of Spring Boot app you want to compile as GraalVM Native Image! Therefor the best way is to get some inspiration from the example projects of the spring-graal-native project:

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;

每个参数在latest docs or this blog post中也有全面的解释。

最后通过 ./compile.sh 执行 bash 脚本并喝杯咖啡!这需要一些时间,具体取决于您的硬件!在我后期的 MBP 2017 中,示例项目大约需要 3-4 分钟。如果一切顺利,您将在 /target/native-image/spring-boot-graal 中找到本机编译的 Spring 启动应用程序。只需 运行 它与:

./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 在 native-image-maven-plugin 使用时不会自动探索所需的 Spring 组件(这是一个错误吗?),我们需要明确地添加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 配置文件(请参阅 this 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 标签,它需要继承 compile.sh 脚本中看到的 native-image 命令的参数。与那个相比,我们可以省略 -cp $CP $MAINCLASS 参数,因为插件识别 class 路径,包括 mainclass 本身(仅后者,如果 start-class 标签来自步骤 3 已设置)。使用 <imageName>${project.artifactId}</imageName> 是一个好主意,以便将我们的 artifactId 用于生成的可执行映像名称。

现在只需通过以下方式执行 Maven 配置文件:

mvn -Pnative clean package

如果编译成功,启动你的本地 Spring 引导应用程序:

./target/spring-boot-graal

================================

如果你想 运行 在像 TravisCI 这样的 CI 服务器上编译本机图像,或者使用 Docker 进行编译,我可以 and this blog post. See the full compile process in action on TravisCI also.

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

Spring Native beta that we (the Spring team) have just announced with a very detailed blog post and a video.

的发布提供了这个问题的最新答案。

它允许您通过 Spring Boot mvn spring-boot:build-imagegradle bootBuildImage 命令,或仅利用通过 native-image-maven-plugin.

的本地 native-image 安装

最有用的链接是start.spring.io which now provides Spring Native support and the getting started section of the reference documentation

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

尽情享受吧!