GraalVM和Spring应用程序 [英] GraalVM and Spring Applications
问题描述
GraalVM系统显然无法将Spring应用程序编译为本地映像.
The GraalVM system apparently cannot compile a Spring application into a native image.
我们可以编译Spring应用程序的一个子集(例如,作为一个单独的库),然后将其与使用常规javac编译器编译的其余部分一起使用吗?
Can we compile a subset of the Spring application -- say, as a separate library -- and then use that with the remainder compiled using the usual javac compiler?
或者,如果我们从应用程序中遗漏了一些Spring功能?
Or maybe if we leave out some Spring features from our application?
还有其他可能性吗?
推荐答案
已经 OlegŠelajev指出,使用 GraalVM本机映像(其中是Spring Boot应用程序本机编译)是 GraalVM 的子项目,现在可能有限制,并且实现反应式Spring Boot Web应用程序的示例项目中1.5 seconds
到0.08 seconds
As 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 Boot版本.如果您想开始尝试使用该功能,则可以立即开始.
In short, if you want to use that feature in production you have to wait for the final Spring 5.3 release in late 2020 and the Spring Boot release which is based upon it. If you want to already start using that feature experimentally, you can start right now.
====== Updated to spring-graalvm-native 0.7.0 release at June 10, 2020. =======
以下是基本步骤(2020年6月), ://github.com/spring-projects-experimental/spring-graalvm-native"rel =" nofollow noreferrer> spring-projects-实验项目spring-graalvm-native 和
Here are the basic steps (June 2020), derived from the latest docs of the spring-projects-experimental project spring-graalvm-native and this blog post I recently wrote (step 7 & 8 could be either achieved with a compile.sh
bash script or with the help of the native-image-maven-plugin
- both alternatives are explained below):
- 编写您的Spring Boot应用程序(例如从 https://start.spring.io 重新开始或使用现有应用程序)
- 在pom.xml中升级到最新版本的Spring Boot 2.3.x(不要从2.2或更低版本开始!)
- 在pom.xml中设置开始类元素
- Write your Spring Boot app (like starting over at https://start.spring.io or use an existing application)
- Upgrade to a recent Release of Spring Boot 2.3.x (do not start with 2.2 or lower!) inside your pom.xml
- Setting start-class element in pom.xml
稍后,native-image
命令需要与@SpringBootApplication
带注释的类的完全合格的类名完全相同.像这样在pom.xml
的属性中定义它:
The native-image
command later needs the exact fully qualified class name of your @SpringBootApplication
annotated class. Define it in your pom.xml
's properties like this:
<properties>
...
<start-class>io.jonashackt.springbootgraal.SpringBootHelloApplication</start-class>
</properties>
- 禁止使用GCLIB代理
由于GraalVM不支持GCLIB代理,因此Spring Boot需要改用JDK代理.因此,请使用@SpringBootApplication类的proxyBeanMethods = false
属性:
As GraalVM doesn't support GCLIB proxies, Spring Boot needs to use JDK proxies instead. Therefore use the proxyBeanMethods = false
property of your @SpringBootApplication class:
@SpringBootApplication(proxyBeanMethods = false)
public class SpringBootHelloApplication {
...
}
- 安装GraalVM 20.1.0和GraalVM本机映像命令
最简单的方法是使用SDKMAN:
The easiest way to do this is to use 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
来检查两者是否都能正常工作.有关更多详细信息,请参见此博客文章.
Check if both works correctly by typing java -version
(GraalVM should be listed) and native-image --version
. See this blog post for more details.
- 从运行时重定位注释类路径扫描以构建时间& 6.检测自动配置
这两个步骤都是由Spring Graal @AutomaticFeature为您完成的,稍后与native-image
命令一起使用.由于@AutomaticFeature 已发布Spring Milestones存储库,我们可以简单地向我们的pom.xml
添加一个依赖项(别忘了现在还添加Spring Milestones存储库,因为现在还没有通过Maven Central发行):
Both steps are done for you by the Spring Graal @AutomaticFeature used later with the native-image
command. As the @AutomaticFeature was already released on Spring Milestones repository, we can simply add a dependency to our pom.xml
(don't forget to also add Spring Milestones repository for now, since this isn't shipped via Maven Central right now):
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graalvm-native</artifactId>
<version>0.7.0</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&配置类路径.我创建了一个 compile.sh 来执行必要的操作bash的步骤:
In essence, we need to prepare configuration variables for native-image
command, then build the app, expand the Spring Boot fat JAR & configure the classpath. I created a compile.sh that executes the necessary steps with 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
- 制作native-image命令并运行编译
现在,我们几乎已经准备好要制作的所有东西,并最终运行native-image
命令.这是一个示例,该示例基于提到的实现反应式Spring Boot Web应用程序的示例项目.这是一个棘手的问题,它取决于您要编译为GraalVM本机映像的Spring Boot应用程序的类型!因此,最好的方法是从
Now we have mostly everything prepared to craft and finally run the native-image
command. Here's an example, which is based on the mentioned 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.graal.remove-unused-autoconfig=true \
-Dspring.graal.remove-yaml-support=true \
-cp $CP $MAINCLASS;
此博客文章中的每个参数.
最后通过./compile.sh
执行bash脚本,然后喝杯咖啡!这需要一些时间,具体取决于您的硬件!在我后期的MBP 2017中,示例项目大约需要3-4分钟.如果一切顺利,您可以在/target/native-image/spring-boot-graal
中找到本地编译的Spring Boot应用程序.只需使用以下命令即可运行它:
Finally execute the bash script via ./compile.sh
and grab a coffee! This takes some time depending on your hardware! On my late MBP 2017 this takes around 3-4 minutes for the example project. If everything went fine, you'll find your natively compiled Spring Boot app in /target/native-image/spring-boot-graal
. Simply run it with:
./target/native-image/spring-boot-graal
=============================
==============================
替代7& 8:native-image-maven-plugin
Alternatively to the bash script (and described steps 7 & 8) there's also the native-image-maven-plugin. But please only use it, if you're really sure how to configure the native-image
command - since it's execution is quite cumbersome right now (I'am sure there will be many improvements until late 2020). If you want to use the plugin, the steps instead of 7 & 8 are as follows:
- 添加spring-context-indexer依赖项
由于Spring @AutomaticFeature在被native-image-maven-plugin
使用时不会自动探索所需的Spring组件(这是一个错误吗?),因此我们需要明确地添加spring-context-indexer
来完成工作:
As the Spring @AutomaticFeature doesn't automatically explore the needed Spring Components while used by the native-image-maven-plugin
(is this a bug?), we need to explicitely add the spring-context-indexer
to do the job:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
</dependency>
它将创建一个target/classes/META_INF/spring.components
文件,然后由本机图像编译过程将其拾取.
It creates a target/classes/META_INF/spring.components
file which then is picked up by the native image compilation process.
- 将native-image-maven-plugin添加为Maven构建配置文件
为了获取此pom.xml作为完整的示例):
In order to get the native-image-maven-plugin working, it is good practice to create a new Maven profile for the native image compilation (see this pom.xml for a fully working example):
<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.graal.remove-unused-autoconfig=true -Dspring.graal.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
,因为它为本地图像插件准备了必要的配置.
We need to add the spring-boot-maven-plugin
again, because it prepares neccessary configuration for the native image plugin.
关键部分是buildArgs
标记,它需要继承native-image
命令的参数,如compile.sh
脚本所示.与之相比,我们可以省去-cp $CP $MAINCLASS
参数,因为该插件可以识别包括主类本身的类路径(仅当设置了第3步中的start-class
标记时,才可以包含主类本身).为了将我们的artifactId
用作生成的可执行映像名称,使用<imageName>${project.artifactId}</imageName>
是一个好主意.
The crucial part is the buildArgs
tag which needs to inherit the parameters for the native-image
command as seen in the compile.sh
script. Compared to that one we can leave out the -cp $CP $MAINCLASS
parameter, since the plugin recognises the classpath including the mainclass itself (the latter only, if the start-class
tag from step 3 is set). Using <imageName>${project.artifactId}</imageName>
is a good idea in order to use our artifactId
for the resulting executable image name.
现在只需通过以下命令执行Maven配置文件:
Now simply execute the Maven profile via:
mvn -Pnative clean package
如果编译成功,请使用以下命令启动本机Spring Boot应用程序:
If the compilation succeeded, start your native Spring Boot app with:
./target/spring-boot-graal
=============================
==============================
如果您想在像TravisCI这样的CI服务器上运行本机映像编译,或者使用Docker进行编译,我可以建议这样做答案和此博客文章一个>.参见在TravisCI上运行的完整编译过程
If you want to run the native image compilation on a CI server like TravisCI or use Docker to do the compilation I could recomment this so answer and this blog post. See the full compile process in action on TravisCI also.
这篇关于GraalVM和Spring应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!