1. 安装GraalVM
需要使用GraalVM,需要在Oracle官网下载支持GraalVM的JDK,https://www.oracle.com/java/technologies/downloads/ 。
下载完成之后需要配置环境变量GRAALVM_HOME,指向GraalVM的家目录,后面Native包的编译过程当中会是用到GRAALVM_HOME环境变量。
export GRAALVM_HOME=/home/to/graalvm-jdk-22.0.2+9.1/Contents/Home
2. 基于Gradle新建SpringBoot工程
Gradle版本使用最新的8.8版本,Gradle相关的配置使用依赖如下:
plugins {
kotlin("jvm") version "1.9.24"
kotlin("plugin.spring") version "1.9.24"
id("org.springframework.boot") version "3.3.2"
id("io.spring.dependency-management") version "1.1.6"
id("org.graalvm.buildtools.native") version "0.10.2"
}
group = "com.wanna.project"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
这里用到了核心的id("org.graalvm.buildtools.native") version "0.10.2"
这个插件,这个插件是用于生成Gradle任务的,用于方便给我们在Gradle当中进行GraalVM AOT的二进制机器码的编译。
项目当中只有如下两个类
@SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
@RestController
class HelloController {
@RequestMapping("/hello")
fun hello(): String {
return "OK"
}
}
打成FatJar包,发现启动时长在1s左右。
2024-08-20T01:45:01.910+08:00 INFO 49663 --- [com.wanna.project.native] [ main] c.w.project.nativeproject.ApplicationKt : Started ApplicationKt in 0.985 seconds (process running for 1.256)
查看打成的SpringBoot的FatJar包大小,大概在25.6M。
-rw-r--r-- 1 ... staff 25606167 Aug 20 01:46 nativeproject-0.0.1-SNAPSHOT.jar
3.尝试对自己的项目使用GraalVM进行AOT编译
3.1 执行GraalVM AOT编译
安装好JDK之后,在自己的项目下,可以使用如下的命令进行本地的GraalVM的AOT编译:
./gradlew nativeCompile
接着就遇到了第一个问题:
Determining GraalVM installation failed with message: 'gu' at '.../jdk-21.0.3.jdk/Contents/Home/bin/gu' tool wasn't found. This probably means that JDK at isn't a GraalVM distribution.
有可能是以下的几种情况:
- 1.下载的并不是支持GraalVM的JDK。
- 2.环境变量配置有问题,比如出现下面的情况,就得观察提示的路径是否是预期的GraalVM配置。
如果正常的话,应该是会有类似如下的这样的编译进度(编译会很慢,需要耐心等待)。
> Task :processAot
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.2)
2024-08-20T02:33:40.810+08:00 INFO 49347 --- [com.wanna.project.native] [ main] c.w.project.nativeproject.ApplicationKt : Starting ApplicationKt using Java 21.0.4 with PID 49347 (/Users/jianchaojia/Desktop/Code/java/project/com.wanna.project.native/build/classes/kotlin/main started by jianchaojia in /Users/jianchaojia/Desktop/Code/java/project/com.wanna.project.native)
2024-08-20T02:33:40.812+08:00 INFO 49347 --- [com.wanna.project.native] [ main] c.w.project.nativeproject.ApplicationKt : No active profile set, falling back to 1 default profile: "default"
> Task :generateResourcesConfigFile
[native-image-plugin] Resources configuration written into /Users/jianchaojia/Desktop/Code/java/project/com.wanna.project.native/build/native/generated/generateResourcesConfigFile/resource-config.json
> Task :nativeCompile
[native-image-plugin] GraalVM Toolchain detection is disabled
[native-image-plugin] GraalVM location read from environment variable: GRAALVM_HOME
[native-image-plugin] Native Image executable path: /Users/jianchaojia/Desktop/Code/jdk/graalvm-jdk-22.0.2+9.1/Contents/Home/lib/svm/bin/native-image
Warning: The option '-H:ResourceConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomcat-embed-core/tomcat-resource.json' is experimental and must be enabled via '-H:+UnlockExperimental
执行完成之后,会有如下的提示,编译成功,且编译时长花费了1分42秒。
Build artifacts:
/Users/jianchaojia/Desktop/Code/java/project/com.wanna.project.native/build/native/nativeCompile/nativeproject (executable)
========================================================================================================================
Finished generating 'nativeproject' in 1m 42s.
[native-image-plugin] Native Image written to: /Users/jianchaojia/Desktop/Code/java/project/com.wanna.project.native/build/native/nativeCompile
提示你可以在build/native/nativeCompile/
目录下找到二进制文件,我们可以找到该文件,像普通软件一样只需要双击即可执行。
双击之后,会有如下的启动SpringBoot的过程,和正常的SpringBoot启动没什么区别
$ build/native/nativeCompile/nativeproject
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.2)
2024-08-20T02:38:45.591+08:00 INFO 49501 --- [com.wanna.project.native] [ main] c.w.project.nativeproject.ApplicationKt : Starting AOT-processed ApplicationKt using Java 22.0.2 with PID 49501
......
2024-08-20T02:38:45.630+08:00 INFO 49501 --- [com.wanna.project.native] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2024-08-20T02:38:45.630+08:00 INFO 49501 --- [com.wanna.project.native] [ main] c.w.project.nativeproject.ApplicationKt : Started ApplicationKt in 0.057 seconds (process running for 0.081)
启动只需要0.05s,启动速度相比使用FatJar的方式提升了20倍。
我们查看该文件,发现该二进制包在Macos下大小为92.3M,对比Jar包的大小25.6M,包的大小翻了大约3.6倍左右。
$ ls -al build/native/nativeCompile/
total 180464
drwxr-xr-x 3 jianchaojia staff 96 Aug 20 02:35 .
drwxr-xr-x 5 jianchaojia staff 160 Aug 20 02:33 ..
-rwxr-xr-x 1 jianchaojia staff 92394384 Aug 20 02:35 nativeproject
3.2 IDEA的Gradle无法识别到GRAALVM_HOME环境变量
我在本地通过gradlew命令手动执行能通过,但是我尝试通过Gradle当中运行时,一直找不到我配置的GRAALVM_HOME环境变量,一直提示我使用的JDK不是GRAALVM的JDK,是别的JDK路径。
一直提示下面的内容,具体原因不知道。
Determining GraalVM installation failed with message: 'gu' at '.../jdk-21.0.3.jdk/Contents/Home/bin/gu' tool wasn't found. This probably means that JDK at isn't a GraalVM distribution.
接着,我尝试Debug Gradle的编译过程,我通过System.getenv尝试去获取这个环境变量,发现确实没这个环境变量。
接着我尝试通过修改IDEA的Gradle的运行配置,去手动添加环境变量:
GRAALVM_HOME=/Users/jianchaojia/Desktop/Code/jdk/graalvm-jdk-22.0.2+9.1/Contents/Home
再次尝试进行nativeCompile编译,发现IDEA的Gradle AOT编译正常。
为什么IDEA启动的Gradle没有GRAALVM_HOME环境变量?因为IDEA启动的进程会继承IDEA进程的环境变量,而IDEA会继承操作系统的环境变量,但是通过查看IDEA继承的环境变量发现,继承操作系统的环境变量时就缺少了GRAALVM_HOME。
所以这是因为IDEA没有继承环境变量导致的,那么为什么没继承呢?因为JAVA进程在启动之后,环境变量就被缓存下来了,是不可变的,如果在IDEA运行中更改环境变量,那么对IDEA是不生效的,所以如果修改了环境变量,需要重启IDEA才能生效。
重启IDEA之后,重新查看环境变量生效。