简介

在之前spring boot3文章中咱们介绍了,spring boot3的一个重要个性就是反对把spring boot3的利用编译成为GraalVM的Native Image。

明天咱们用具体的例子来给大家演示一下如何正确的将spring boot3的利用编译成为native image。

装置GraalVM

如果要把spring boot3的app编译成为native利用,须要GraalVM的反对。

什么是GraalVM呢?

从名字就可以看进去GraalVM是一个虚拟机,它的次要指标就是晋升java应用程序的性能,并且耗费更少的资源。

它在java HotSpot JVM的根底上增加了JIT编译器和AOT来实现将利用编译成为本地可执行文件。除了java之外,GraalVM还反对JavaScript、Ruby、Python等多种编程语言。

所以,为什么要用GraalVM呢?一个字:快。

装置GraalVM也比较简单,咱们进入它的官网下载页面下载对应的版本即可:https://www.oracle.com/downlo...。

GraalVM跟JDK一样也有两个版本,社区版和企业版本,大家能够依据须要自行抉择。

要留神的是spring boot3须要GraalVM 22.3以上的版本反对,大家可不要下载错了。

下载实现之后,咱们能够像失常装置JDK一样来装置GraalVM,这里以mac为例,如果咱们装置的目录是/Library/Java/JavaVirtualMachines/graalvm-ee-java17-22.3.0,那么咱们须要配置对应的JAVA_HOME和PATH环境变量如下:

 export PATH=/Library/Java/JavaVirtualMachines/graalvm-ee-java17-22.3.0/Contents/Home/bin:$PATH export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-ee-java17-22.3.0/Contents/Home

PATH中有一个十分重要的命令叫做gu,如果不增加PATH,那么在应用中就可能遇到上面的异样:

'gu' tool wasn't found. This probably means that JDK at isn't a GraalVM distribution.

装置结束之后能够通过上面的命令来进行验证:

java -versionjava version "17.0.5" 2022-10-18 LTSJava(TM) SE Runtime Environment GraalVM EE 22.3.0 (build 17.0.5+9-LTS-jvmci-22.3-b07)Java HotSpot(TM) 64-Bit Server VM GraalVM EE 22.3.0 (build 17.0.5+9-LTS-jvmci-22.3-b07, mixed mode, sharing)

如果是在mac环境下,还须要执行上面的命令来解除对graalvm的隔离限度:

 sudo xattr -r -d com.apple.quarantine /path/to/graalvm

否则在应用中就会遇到上面的问题:

增加Native Image反对

咱们装置GraalVM的目标就是应用它的native Image个性。native image是一个独自的jar包,咱们能够执行上面的命令来进行装置:

gu install native-image

其中gu就是/Library/Java/JavaVirtualMachines/graalvm-ee-java17-22.3.0/Contents/Home/bin中的命令。

下载的过程中还须要输出一个无效的邮件,并进行邮箱校验。而后一路ENTER就能够了。

当然,你还能够把Oracle GraalVM Enterprise Edition Native Image下载到本地,而后应用gu install -L来进行本地装置。

好了,到目前为止,所有筹备得当,咱们接下来看看如何把spring boot3的利用打包成为native image吧。

构建spring boot3利用

这里咱们应用的是maven,所以须要增加上面的spring boot3的依赖:

    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>3.0.1</version>        <relativePath/>     </parent>

因为要构建native image,所以咱们还须要用到上面的一个native-maven-plugin插件:

            <plugin>                <groupId>org.graalvm.buildtools</groupId>                <artifactId>native-maven-plugin</artifactId>            </plugin>

这里咱们只创立了一个非常简单的main办法:

@SpringBootApplicationpublic class NativeImageApplication {    public static void main(String[] args) {        SpringApplication.run(NativeImageApplication.class, args);    }}

而后,咱们尝试运行 mvn native:build来构建spring boot3应用程序。

记得在build之前肯定先要编译好我的项目。

很惋惜,你会发现上面的异样:

[INFO] --- native-maven-plugin:0.9.19:build (default-cli) @ native-image ---[WARNING] 'native:build' goal is deprecated. Use 'native:compile-no-fork' instead.[INFO] Found GraalVM installation from JAVA_HOME variable....Error: Please specify class (or <module>/<mainclass>) containing the main entry point method. (see --help)

从下面的异样咱们发现了两个问题,第一个问题是一个正告,它举荐咱们应用native:compile-no-fork。

第二个问题是说找不到mainclass,依据异样信息,咱们在pom的plugin中增加上面的配置信息,如下所示:

<plugin>                <groupId>org.graalvm.buildtools</groupId>                <artifactId>native-maven-plugin</artifactId>                <configuration>                    <!-- imageName用于设置生成的二进制文件名称 -->                    <imageName>${project.artifactId}</imageName>                    <!-- mainClass用于指定main办法类门路 -->                    <mainClass>com.flydean.nativeimage.NativeImageApplication</mainClass>                    <buildArgs>                        --no-fallback                    </buildArgs>                </configuration>                <executions>                    <execution>                        <id>build-native</id>                        <goals>                            <goal>compile-no-fork</goal>                        </goals>                        <phase>package</phase>                    </execution>                </executions>            </plugin>

而后从新运行mvn native:compile-no-fork:

GraalVM Native Image: Generating 'native-image' (executable)...========================================================================================================================[1/7] Initializing...                                                                                    (4.3s @ 0.25GB) Version info: 'GraalVM 22.3.0 Java 17 EE' Java version info: '17.0.5+9-LTS-jvmci-22.3-b07' C compiler: cc (apple, arm64, 14.0.0) Garbage collector: Serial GC 1 user-specific feature(s) - org.springframework.aot.nativex.feature.PreComputeFieldFeatureField org.apache.commons.logging.LogAdapter#log4jSpiPresent set to true at build timeField org.apache.commons.logging.LogAdapter#log4jSlf4jProviderPresent set to true at build timeField org.apache.commons.logging.LogAdapter#slf4jSpiPresent set to true at build timeField org.apache.commons.logging.LogAdapter#slf4jApiPresent set to true at build timeField org.springframework.core.NativeDetector#imageCode set to true at build timeField org.springframework.core.KotlinDetector#kotlinPresent set to false at build timeField org.springframework.core.KotlinDetector#kotlinReflectPresent set to false at build timeField org.springframework.format.support.DefaultFormattingConversionService#jsr354Present set to false at build timeField org.springframework.cglib.core.AbstractClassGenerator#imageCode set to true at build time[2/7] Performing analysis...  [**********]                                                              (24.8s @ 4.57GB)  10,266 (89.50%) of 11,470 classes reachable  16,675 (63.53%) of 26,248 fields reachable  53,776 (60.71%) of 88,575 methods reachable     469 classes,   140 fields, and 2,281 methods registered for reflection      63 classes,    69 fields, and    55 methods registered for JNI access       5 native libraries: -framework CoreServices, -framework Foundation, dl, pthread, z[3/7] Building universe...                                                                               (5.0s @ 2.72GB)[4/7] Parsing methods...      [**]                                                                       (4.4s @ 2.42GB)[5/7] Inlining methods...     [***]                                                                      (1.3s @ 3.87GB)[6/7] Compiling methods...    [********]                                                                (70.0s @ 1.04GB)[7/7] Creating image...                                                                                  (4.7s @ 3.35GB)  30.27MB (58.75%) for code area:    30,771 compilation units  20.50MB (39.79%) for image heap:  305,579 objects and 93 resources 769.52KB ( 1.46%) for other data  51.52MB in total------------------------------------------------------------------------------------------------------------------------Top 10 packages in code area:                               Top 10 object types in image heap:   2.02MB com.oracle.svm.core.code                             5.79MB byte[] for code metadata   1.77MB sun.security.ssl                                     2.31MB byte[] for java.lang.String   1.29MB java.util                                            2.09MB byte[] for general heap data 929.52KB java.lang.invoke                                     2.07MB java.lang.String 925.96KB com.sun.crypto.provider                              1.76MB java.lang.Class 802.99KB java.lang                                          671.09KB byte[] for embedded resources 633.35KB sun.nio.ch                                         567.26KB byte[] for reflection metadata 625.89KB java.util.concurrent                               481.22KB com.oracle.svm.core.hub.DynamicHubCompanion 601.86KB org.apache.tomcat.util.net                         450.06KB java.util.HashMap$Node 594.48KB sun.security.x509                                  401.78KB java.util.concurrent.ConcurrentHashMap$Node  20.02MB for 397 more packages                                3.40MB for 2297 more object types------------------------------------------------------------------------------------------------------------------------                        9.5s (7.9% of total time) in 50 GCs | Peak RSS: 3.75GB | CPU load: 4.39------------------------------------------------------------------------------------------------------------------------Produced artifacts: /Users/learn-springboot3/learn-springboot3/native-image/target/native-image (executable) /Users/learn-springboot3/learn-springboot3/native-image/target/native-image.build_artifacts.txt (txt)========================================================================================================================Finished generating 'native-image' in 2m 0s.[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time:  02:01 min[INFO] Finished at: 2023-01-05T20:43:39+08:00[INFO] ------------------------------------------------------------------------

通过漫长的期待,咱们终于build实现了。

因为咱们的artifactId叫做native-image,所以最终在target目录上面生成了一个叫做native-image的可执行文件:

.├── classes│   ├── application.properties│   └── com│       └── flydean│           └── nativeimage│               └── NativeImageApplication.class├── generated-sources│   └── annotations├── generated-test-sources│   └── test-annotations├── maven-archiver│   └── pom.properties├── maven-status│   └── maven-compiler-plugin│       ├── compile│       │   └── default-compile│       │       ├── createdFiles.lst│       │       └── inputFiles.lst│       └── testCompile│           └── default-testCompile│               ├── createdFiles.lst│               └── inputFiles.lst├── native-image├── native-image-0.0.1-SNAPSHOT.jar├── native-image-0.0.1-SNAPSHOT.jar.original├── native-image.build_artifacts.txt├── surefire-reports│   ├── TEST-com.flydean.nativeimage.NativeImageApplicationTests.xml│   └── com.flydean.nativeimage.NativeImageApplicationTests.txt└── test-classes    └── com        └── flydean            └── nativeimage                └── NativeImageApplicationTests.class20 directories, 14 files

如果你这时候运行target/native-image,那么很可能失去上面的异样:

[main] DEBUG org.springframework.context.aot.AotApplicationContextInitializer - Initializing ApplicationContext with AOT[main] ERROR org.springframework.boot.SpringApplication - Application run failedjava.lang.IllegalArgumentException: Could not find class [com.flydean.nativeimage.NativeImageApplication__ApplicationContextInitializer]        at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:333)

这是因为咱们短少一些spring boot的AOT元文件信息,正确的做法是应用上面的命令:

mvn clean package -Pnative

它实际上执行的是上面的几个命令:

mvn spring-boot:process-aotmvn spring-boot:process-test-aotmvn spring-boot:build-image

最终咱们失去编译好的native-image信息,运行失去上面的后果:

2023-01-05T17:07:11.692+08:00  INFO 69299 --- [           main] c.f.nativeimage.NativeImageApplication   : Starting AOT-processed NativeImageApplication using Java 17.0.5 with PID 69299 (/Users/wayne/data/git/ddean2009/learn-springboot3/learn-springboot3/native-image/target/native-image started by wayne in /Users/wayne/data/git/ddean2009/learn-springboot3/learn-springboot3/native-image)2023-01-05T17:07:11.693+08:00  INFO 69299 --- [           main] c.f.nativeimage.NativeImageApplication   : No active profile set, falling back to 1 default profile: "default"2023-01-05T17:07:11.709+08:00  INFO 69299 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)2023-01-05T17:07:11.710+08:00  INFO 69299 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]2023-01-05T17:07:11.710+08:00  INFO 69299 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.4]2023-01-05T17:07:11.717+08:00  INFO 69299 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext2023-01-05T17:07:11.717+08:00  INFO 69299 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 24 ms2023-01-05T17:07:11.729+08:00  INFO 69299 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''2023-01-05T17:07:11.729+08:00  INFO 69299 --- [           main] c.f.nativeimage.NativeImageApplication   : Started NativeImageApplication in 0.053 seconds (process running for 0.072)

总结

从运行状况来看,native-image的启动速度十分快,应该能够晋升不少的性能。

感兴趣的小伙伴连忙用起来吧。

本文的例子https://github.com/ddean2009/learn-springboot3