乐趣区

关于java:在spring-boot3中使用native-image

简介

在之前 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 -version
java version "17.0.5" 2022-10-18 LTS
Java(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 办法:

@SpringBootApplication
public 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.PreComputeFieldFeature
Field org.apache.commons.logging.LogAdapter#log4jSpiPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#log4jSlf4jProviderPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#slf4jSpiPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#slf4jApiPresent set to true at build time
Field org.springframework.core.NativeDetector#imageCode set to true at build time
Field org.springframework.core.KotlinDetector#kotlinPresent set to false at build time
Field org.springframework.core.KotlinDetector#kotlinReflectPresent set to false at build time
Field org.springframework.format.support.DefaultFormattingConversionService#jsr354Present set to false at build time
Field 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.class

20 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 failed
java.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-aot
mvn spring-boot:process-test-aot
mvn 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 WebApplicationContext
2023-01-05T17:07:11.717+08:00  INFO 69299 --- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 24 ms
2023-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

退出移动版