简介:GraalVM 是一个跨语言的通用虚拟机,不仅支持了 Java、Scala、Groovy、Kotlin 等基于 JVM 的语言,以及 C、C++ 等基于 LLVM 的语言,还支持其它像 JavaScript、Ruby、Rust、Python 和 R 语言等。它消除了编程语言之间的隔离。
GraalVM EE <==== 内置 Oarcle JDK 版本
Oracle GraalVM 企业版是Oralce Java SE 订阅的一部分,无需额外付费,但众多周知除了JDK8u202及以下版本和JDK17版本不收费,Oracle JDK的其他版本是收费的。
GraalVM CE <==== 内置 Open JDK 版本
社区版,免费
Spring支持Graalvm native-image 功能最低要求 springboot3.x ,而支持Springboot3.x功能的最低要求jdk17+ ,所以Graalvm native-image实践的难点在于 JDK8 + springboot2.x + JavaEE 向 JDK17 + springboot3.x + Jakarta EE的项目迁移。
1、参考文档
Springboot3.0.2参考文档
https://docs.spring.io/spring-boot/docs/3.0.2/reference/pdf/spring-boot-reference.pdf
https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/
Springboot3.x环境要求
Graalvm22.x native-image参考文档
https://www.graalvm.org/latest/reference-manual/native-image/
Quarkus 集成native-image参考文档(仅供参考)
https://quarkus.io/guides/building-native-image#producing-a-native-executable
Graalvm最新版官网下载地址
https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-22.3.1
微软VS工具官网下载地址
https://learn.microsoft.com/zh-cn/visualstudio/releases/2022/release-history
https://learn.microsoft.com/zh-cn/visualstudio/releases/2019/history#installing-an-earlier-release
Maven历史版本下载地址
https://archive.apache.org/dist/maven/maven-3/
IDEA 历史版本下载地址
https://www.jetbrains.com/idea/download/other.html
根据以上官方文档的参考,我们选择以下环境
Win10 21H2
Graalvm Community 22.3.1(集成JDK17.06)
Maven3.8.1
IDEA Community 2022.2.4
(兼容JDK8的最后一个版本,且支持JDK17和Springboot3,默认Maven3.8.1)
VSBuildTools Community 2022
(官网要求VisualStudio2017 和 VsBuildTools2017以上版本,个人实践发现只用VSBuildTools工具也能满足需求)
2、环境搭建介质准备
graalvm-ce-java17-windows-amd64-22.3.1.zip
native-image-installable-svm-java17-windows-amd64-22.3.1.jar
vs_BuildTools.exe
apache-maven-3.8.6.zip
ideaIC-2022.2.4.exe
3、Graalvm和Maven安装
graalvm-ce-java17-windows-amd64-22.3.1.zip
解压,配置环境变量
GRAALVM_HOME= D:\ProgramFiles\Java\graalvm-ce-java17-22.3.1
JAVA_HOME= %GRAALVM_HOME%
PATH= %JAVA_HOME%\bin
查看当前Graalvm版本
java -vesrion
gu --version
离线安装native-image工具
gu install -L native-image-installable-svm-java17-windows-amd64-22.3.1.jar
查看安装结果
gu list
apache-maven-3.8.6.zip解压安装配置环境变量
M2_HOME= D:\ProgramFiles\Java\apache-maven-3.8.6
PATH=%M2_HOME%\bin;
mvn -v 查看当前版本
4、VsBuildTool安装
注意上述标记的勾选项部分!!!
(1)确认vcvars64.bat文件是否存在
C:\ProgramFiles (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat
对应VS2022控制台快捷方式
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\VisualStudio 2022\Visual Studio Tools\VC\ x64 Native Tools Command Prompt for VS 2022
(2)确认以下环境变量目录是否存在,并配置对应环境变量
LIB环境变量
C:\ProgramFiles (x86)\Windows Kits\10\Lib\10.0.20348.0\um\x64;
C:\ProgramFiles (x86)\Windows Kits\10\Lib\10.0.20348.0\ucrt\x64;
C:\ProgramFiles (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.34.31933\lib\x64;
INCLUDE环境变量
C:\ProgramFiles (x86)\Windows Kits\10\Include\10.0.20348.0\ucrt;
C:\ProgramFiles (x86)\Windows Kits\10\Include\10.0.20348.0\um;
C:\ProgramFiles (x86)\Windows Kits\10\Include\10.0.20348.0\shared;
C:\ProgramFiles (x86)\Microsoft VisualStudio\2022\BuildTools\VC\Tools\MSVC\14.34.31933\include;
PATH环境变量
C:\ProgramFiles (x86)\Microsoft VisualStudio\2022\BuildTools\VC\Tools\MSVC\14.34.31933\bin\HostX64\x64;
如果上述校验失败,则说明VSbuildTool环境安装时选的组件有缺失,建议完全卸载后重新安装,完全卸载包含正常卸载+删除对应下载目录或缓存目录+清理注册表重启。
5、验证VS环境和native-image工具
创建D:\\test 项目工程目录(注意native-image是基于项目工程目录的,直接D盘下创建HelloWorld.java文件进行下述操作,会将整个D盘作为项目工程目录导致可能发生下图错误)
创建D:\\test\\HelloWorld.java 文件,文件内容如下
publicclass HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, NativeWorld!");
}
}
进入项目工程目录下
执行
javac HelloWorld.java
再执行
native-imageHelloWorld
生成可执行文件如上图,此操作证明了你的VS和Graalvmnative-image环境是正常的
6、IDEA创建springboo3项目并集成native-image
创建完毕后我们发现默认的native-maven-plugin插件版本为0.9.19版本,此版本创建exe可执行文件对应的命令为 mvn -Pnative native:compile
CMD控制台执行
call "C:\ProgramFiles (x86)\Microsoft VisualStudio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
或者打开控制台
x64 NativeTools Command Prompt for VS 2022
根据官方文档没有执行vcvars64.bat直接使用普通的控制台执行相关命令会导致编译路径异常
切换到当前项目目录,由于此处是使用0.9.19版本native-maven-plugin插件,我们执行以下命令
mvnw -Pnative native:compile
发现报错如下
[INFO] Executing:D:\graalvm-ce-java17-22.3.1\bin\native-image.cmd@target\tmp\native-image-12396827555653810932.args
Exceptionin thread "main" java.nio.file.InvalidPathException: Illegal char< > at index 3: D:\ IdeaProjects\ demo\ target\ classes
根据错误提示我们查看并修改
D:\springboot-native-demo\target\tmp\native-image-7282593071674994904.args
文件将里面的所有\\路径全局替换为/路径符号。
然后我们手动执行刚才报错的命令
native-image @target\tmp\native-image-7282593071674994904.args
(此处我们也可以将args文件中的参数粘贴出来作为native-image 后面的参数,不需要替换路径,只需要将相关参数合并成单行命令执行也能成功,说明native-image通过vcvars64.bat脚本执行后是支持window的路径\\格式的)
等价于
执行完毕结果
发现成功了,个人猜测这可能是0.9.19版本native-maven-plugin插件的BUG?可能只在window环境发生,因为window环境的路径分隔符和linux不一致,同时我们可以native-image –help查看上述命令的说明,native-maven-plugin插件源码地址如下
https://github.com/graalvm/native-build-tools 查看native-maven-plugin插件源码
native-build-tools\common\utils\src\main\java\org\graalvm\buildtools\utils\NativeImageUtils.java
public staticList<String> convertToArgsFile(List<String> cliArgs, Path outputDir,Path projectDir)
window环境下解决该办法的方法,复制如下定制修改的类NativeImageUtils.java
package org.graalvm.buildtools.utils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* <dependency>
* <groupId>org.graalvm.buildtools</groupId>
* <artifactId>utils</artifactId>
* <version>0.9.19</version>
* </dependency>
* Overlay rewriting org.graalvm.buildtools.utils.NativeImageUtils
*
* @author zhouyanpeng
*/
public class NativeImageUtils {
//javac -Xlint:unchecked NativeImageUtils.java ==> Find unchecked Code line And Remove Chinese Comment
//javac NativeImageUtils.java
//Replace class File ==> /mvn_repo/org/graalvm/buildtools/utils/0.9.19/utils-0.9.19.jar/org/graalvm/buildtools/utils/NativeImageUtils.class
private static final Pattern requiredVersionPattern = Pattern.compile("^([0-9]+)(?:\\.([0-9]+)?)?(?:\\.([0-9]+)?)?$");
private static final Pattern graalvmVersionPattern = Pattern.compile("^GraalVM ([0-9]+)\\.([0-9]+)\\.([0-9]+).*");
public NativeImageUtils() {
}
public static void maybeCreateConfigureUtilSymlink(File configureUtilFile, Path nativeImageExecutablePath) {
if (!configureUtilFile.exists()) {
Path target = configureUtilFile.toPath();
Path source = nativeImageExecutablePath.getParent().getParent().resolve("lib/svm/bin/" + nativeImageConfigureFileName());
if (Files.exists(source, new LinkOption[0])) {
try {
Files.createLink(target, source);
} catch (IOException var5) {
}
}
}
}
public static String nativeImageConfigureFileName() {
//OrgCode ==> return "native-image-configure" + SharedConstants.GRAALVM_EXE_EXTENSION;
return "native-image-configure" + (System.getProperty("os.name", "unknown").contains("Windows") ? ".cmd" : "");
}
public static List<String> convertToArgsFile(List<String> cliArgs, Path outputDir) {
return convertToArgsFile(cliArgs, outputDir, Paths.get(""));
}
/**
* 此处有Window环境下的定制修改
*
* @param cliArgs
* @param outputDir
* @param projectDir
* @returnjava
*/
public static List<String> convertToArgsFile(List<String> cliArgs, Path outputDir, Path projectDir) {
try {
boolean ignored = outputDir.toFile().mkdirs();
File tmpFile = Files.createTempFile(outputDir, "native-image-", ".args").toFile();
//Change NativeImageUtils::escapeArg
cliArgs = cliArgs.stream().map(NativeImageUtils::escapeArg).collect(Collectors.toList());
Files.write(tmpFile.toPath(), cliArgs, StandardCharsets.UTF_8, StandardOpenOption.CREATE);
Path resultingPath = tmpFile.toPath().toAbsolutePath();
if (projectDir != null) {
resultingPath = projectDir.toAbsolutePath().relativize(resultingPath);
}
return Collections.singletonList("@" + resultingPath);
} catch (IOException var6) {
return Collections.unmodifiableList(cliArgs);
}
}
public static String escapeArg(String arg) {
if (!arg.startsWith("\\Q") || !arg.endsWith("\\E")) {
arg = arg.replace("\\", "\\\\");
if (arg.contains(" ")) {
arg = "\"" + arg + "\"";
}
}
//Add This Code To Support Windows Env
if (System.getProperty("os.name").contains("Windows")) {
arg = arg.replace("\\\\", "/");
}
return arg;
}
public static void checkVersion(String requiredVersion, String versionToCheck) {
Matcher requiredMatcher = requiredVersionPattern.matcher(requiredVersion);
if (!requiredMatcher.matches()) {
throw new IllegalArgumentException("Invalid version " + requiredVersion + ", should be for example \"22\", \"22.3\" or \"22.3.0\".");
} else {
Matcher checkedMatcher = graalvmVersionPattern.matcher(versionToCheck.trim());
if (!checkedMatcher.matches()) {
throw new IllegalArgumentException("Version to check '" + versionToCheck + "' can't be parsed.");
} else {
int requiredMajor = Integer.parseInt(requiredMatcher.group(1));
int checkedMajor = Integer.parseInt(checkedMatcher.group(1));
if (checkedMajor < requiredMajor) {
throw new IllegalStateException("GraalVM version " + requiredMajor + " is required but " + checkedMajor + " has been detected, please upgrade.");
} else {
if (requiredMatcher.group(2) != null) {
int requiredMinor = Integer.parseInt(requiredMatcher.group(2));
int checkedMinor = Integer.parseInt(checkedMatcher.group(2));
if (checkedMinor < requiredMinor) {
throw new IllegalStateException("GraalVM version " + requiredMajor + "." + requiredMinor + " is required but " + checkedMajor + "." + checkedMinor + " has been detected, please upgrade.");
}
if (requiredMatcher.group(3) != null) {
int requiredPatch = Integer.parseInt(requiredMatcher.group(3));
int checkedPatch = Integer.parseInt(checkedMatcher.group(3));
if (checkedPatch < requiredPatch) {
throw new IllegalStateException("GraalVM version " + requiredMajor + "." + requiredMinor + "." + requiredPatch + " is required but " + checkedMajor + "." + checkedMinor + "." + checkedPatch + " has been detected, please upgrade.");
}
}
}
}
}
}
}
}
javac NativeImageUtils.java 编译得到NativeImageUtils.class
替换本地maven下载的utils-0.9.19.jar的NativeImageUtils.class
mvn_repo\org\graalvm\buildtools\utils\0.9.19\utils-0.9.19.jar\org\graalvm\buildtools\utils\NativeImageUtils.class
生成的exe文件如下
拷贝springboot-native-demo.exe和application.properties,并修改application.properties配置文件server.port=12345到部署目录D:\\deploy部署目录
双击exe启动项目
发现0.098秒启动,对比普通的java启动时间1.397秒
同时对比普通jar程序的部署(需要安装jdk,执行java命令或脚本等)
Graalvm Native-image真香😄
本文介绍了如何在Windows环境下,使用GraalVM 22.3.1和SpringBoot 3.0.2进行native-image构建。详细阐述了从JDK8+SpringBoot2.x到JDK17+SpringBoot3.x的迁移过程,以及GraalVM的安装、配置,包括设置环境变量、安装native-image工具。文章还涉及到Visual Studio Build Tools 2022的安装与验证,以及解决native-maven-plugin插件在Windows上的路径问题。



9115

被折叠的 条评论
为什么被折叠?



