快捷搜索:  汽车  科技

gvm安装要多少时间(GraalVM安装配置与简单使用)

gvm安装要多少时间(GraalVM安装配置与简单使用)其包含JVM,GraalVM编译器,JavaScript Runtime,LLVM Runtime。选择基于java17的最新版22.0.02。“graalvm-ce-java17-darwin-amd64-22.0.0.2.tar.gz”。下载并解压缩。下载GraalVMGraalVM​社区版下载地址:https://github.com/graalvm/graalvm-ce-builds/releases

说明

JDK自带一个Nashorn脚本引擎来执行JS,但随着时间的推移,也逐渐暴露出不足:

  • 支持的EMACScript版本过低​。Nashorn支持“ECMAScript -262 Edition 5.1”,而非更流行更强大的ES6,也即ECMAScript 2015,更不用说更新的版本了,很多新特性无法使用。对现在更熟悉ES6标准的JS开发者来说也造成了麻烦。
  • ​Oracle官方不再支持Nashorn。Oracle官方宣布Nashorn将在JDK11弃用​,现在JDK17已经正式移除了Nashorn。

其替代品就是GraalVM​。

本文​内容主要描述了:

  1. 如何将GraalVM部署在服务器(Docker方式),运行JS、Java程序(以JAR的方式)、
  2. 作为开发人员,如何在本地基于GRAALVM进行开发。

本地开发

笔者使用的Mac系统,Eclipse​。

下载GraalVM

GraalVM​社区版下载地址:

https://github.com/graalvm/graalvm-ce-builds/releases

选择基于java17的最新版22.0.02。“graalvm-ce-java17-darwin-amd64-22.0.0.2.tar.gz”。下载并解压缩。

其包含JVM,GraalVM编译器,JavaScript Runtime,LLVM Runtime。

版本如下:

% java -version openjdk version "17.0.2" 2022-01-18 OpenJDK Runtime Environment GraalVM CE 22.0.0.2 (build 17.0.2 8-jvmci-22.0-b05) OpenJDK 64-Bit Server VM GraalVM CE 22.0.0.2 (build 17.0.2 8-jvmci-22.0-b05 mixed mode sharing) % js -version GraalVM JavaScript (GraalVM CE Native 22.0.0.2) % lli --version LLVM 12.0.1 (GraalVM CE Native 22.0.0.2)

设置环境变量

设置GraalVM的环境变量与JVM是一样的,只是将JAVA_HOME指向GraalVM所在目录即可。

笔者设置的是~/.zshrc文件,也有设置在.bashrc的。

#这是原JDK8的目录 #JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_311.jdk/Contents/Home #现在指向了GraalVM的目录 JAVA_HOME=/绝对路径/graalvm-ce-java17-22.0.0.2/Contents/Home PATH=$JAVA_HOME/bin:$PATH:. CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:. export JAVA_HOME export CLASSPATH

修改后【source ~/.zshrc】即可生效。在终端命令行处输入上一节的三条命令,就可以看到相关的版本号,证明环境变量设置成功。

设置开发环境

Eclipse按照GraalVM

【Preferences】-【Java 】-【Installed JREs】-【Add】

gvm安装要多少时间(GraalVM安装配置与简单使用)(1)

选择GraalVM目录,并命名:

gvm安装要多少时间(GraalVM安装配置与简单使用)(2)

在要使用GraalVM的项目中指定GraalVM:

【项目右键】-【Build Path】-【Configure Build Path】

gvm安装要多少时间(GraalVM安装配置与简单使用)(3)

在原JDK处选择GraalVM:

gvm安装要多少时间(GraalVM安装配置与简单使用)(4)

设置工程编译版本

在工程的【pom.xml】文件中,一般要指定编译的版本号,通常是:

<plugin> <groupId>org.apache.Maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin>

在我们使用的GraalVM中,内置的JDK是17,此处也要修改如下:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>17</source> <target>17</target> <encoding>UTF-8</encoding> </configuration> </plugin>

完整的pom.xml文件,会在后文提供。

设置maven编译版本

通常都是JDK8,现在也要做更改。

打开Maven的settings.xml,修改如下:

<proFile> <id>jdk-17</id> <activation> <activeByDefault>true</activeByDefault> <jdk>17</jdk> </activation> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion> </properties> </profile>

至此,开发人员本机的系统环境、开发环境、Maven编译环境设置完毕。

代码

本节内容主要介绍如何在GraalVM进行开发、打包,涉及JS、Java代码的运行,相互调用等。

之前基于Nashorn进行开发的代码,都是使用“javax.script.ScriptEngine”,GraalVM也是兼容的,但是官方明确指出这种兼容只是出于遗留原因,官方强烈鼓励使用“org.graalvm.polyglot.Context”,所以本文的代码采用官方推荐的Context进行编码,而非ScriptEngine。

创建一个简单的Maven工程,pom.xml如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.leo</groupId> <artifactId>testgraalvm</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> ​ ​ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.9.0</version> </dependency> ​ <!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency> </dependencies> ​ <build> <finalName>testgraalvm</finalName> <!-- 指定打包生成的文件名 --> <plugins> <!-- 设置编译版本为17 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>17</source> <target>17</target> <encoding>UTF-8</encoding> </configuration> </plugin> ​ <!-- 因为引入了一些第三方JAR,本项目打包后为了顺利执行,将第三方JAR也一同打包 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> <overWriteReleases>false</overWriteReleases> <overWriteSnapshots>false</overWriteSnapshots> <overWriteIfNewer>true</overWriteIfNewer> </configuration> </execution> </executions> </plugin> ​ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <mainClass></mainClass> </manifest> </archive> <!--完整包的后缀名--> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <!-- this is used for inheritance merges --> <phase>package</phase> <!-- 指定在打包节点执行jar包合并操作 --> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>

相关Java类:

User类,目的是Java与JS交互传递实体类数据用。

注意:通常的写法,声明变量用的是“private”,但是在此处必须用“public”,原因后述。

public class User implements Serializable { private static final Long serialVersionUID = 7439853194483038885L; public String name;//必须用public public Integer age; public User() { super(); } public User(String name Integer age) { super(); this.name = name; this.age = age; } //省略GET、SET @Override public String toString() { return "User [name=" name " age=" age "]"; } }

TestUtil类,JS调用Java方法用。

public class TestUtil { public static Integer add(Integer a Integer b) { return a b; } }

JS文件,Java调用JS用。

// JS使用JAVA方法 var ju=Java.type('TestUtil'); function test3(a b){ return ju.add(a b); } // JS将传入的参数整合为JSON,并返回 function test4(name age){ var json={}; json.name=name; json.age=age; return json; }

TestGraalVM类。

import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Source; import org.graalvm.polyglot.TypeLiteral; import org.graalvm.polyglot.Value; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; public class TestGraalVM { public static void main(String[] args) throws IOException { Gson gson = new GsonBuilder().create(); // 必须设置allowAllAccess(true),否则JS无法使用JAVA方法 Context context = Context.newBuilder().allowAllAccess(true).build(); // 获取JS数组的某个值 Value array = context.eval("js" "[1 2 42 4]"); int result = array.getArrayElement(2).asInt(); System.out.println("JS数组第三个数值:" result); // 执行JS Function 无返回结果 System.out.println("=======执行JS Function 无返回结果======="); String jsFunWithoutResult = "function test(a b c){var result= a b c;console.log('JS执行结果:' result);}"; context.eval(Source.create("js" jsFunWithoutResult)); context.getBindings("js").getMember("test").execute(1 2 3); System.out.println("======执行JS内置函数 有返回结果====="); Value vMath = context.getBindings("js"); vMath.putMember("a" 1); vMath.putMember("b" 2); System.out.println("执行JS内置函数:" context.eval(Source.create("js" "Math.max(a b)")).asInt()); // 执行JS Function,并返回结果 System.out.println("=======执行JS Function 有返回结果======="); String jsFun = "function add(x y){var result=x y;console.log('JS返回结果:' result);return result;}"; context.eval(Source.create("js" jsFun)); Long re = context.getBindings("js").getMember("add").execute(10 20).asLong(); System.out.println("执行JS Function,并返回结果: " re); // 获取JS定义的对象、数组。 String jsObj = "var intarr=[1 2 3];var msg = 'hello';var json={'name':'张三' 'age':18};var users=[{'name':'张三' 'age':18} {'name':'李四' 'age':22}]"; context.eval("js" jsObj); System.out.println("======Int数组====="); Value v = context.getBindings("js").getMember("intarr"); TypeLiteral<List<Integer>> INT_LIST = new TypeLiteral<List<Integer>>() { }; List<Integer> ll = v.as(INT_LIST); for (Integer i : ll) { System.out.println("JS定义的INT数组:" i); } System.out.println("JS定义的字符串:" context.getBindings("js").getMember("msg").asString()); System.out.println("=======JS定义的 JSON 实体类========"); User userPojo = gson.fromJson(gson.toJson(context.getBindings("js").getMember("json").as(Map.class)) new TypeToken<User>() { }.getType()); System.out.println(userPojo.toString()); System.out.println("======JS定义的 JSON 实体类列表====="); // JS的JSONArr转为JAVA实体类的写法一 Value uv = context.getBindings("js").getMember("users"); System.out.println("列表总数:" uv.getArraySize()); for (int i = 0; i < uv.getArraySize(); i ) { System.out.println(new User(uv.getArrayElement(i).getMember("name").asString() uv.getArrayElement(i).getMember("age").asInt()).toString()); } // JS的JSONArr转为JAVA实体类的写法二 System.out.println("--------------------"); String jsonList = gson.toJson(context.getBindings("js").getMember("users").as(List.class)); List<User> us = gson.fromJson(jsonList new TypeToken<List<User>>() { }.getType()); for (User user : us) { System.out.println(user.toString()); } // GraalVM官方是很不推荐使用JS访问Java类或文件系统的,所以默认采用了安全的方法 // 但是在我们之前使用Nashorn的时候,因为其支持的ECMAScript版本较低,很多基本特性无法使用,例如没有Map、Set等。为了适应业务需要,有大量JS调用Java对象的代码,此处也写了相关的Demo。 // 不过还是不推荐这么做,GraalVM已经支持ES6,相关特性应该足以替代Java对象。 // 即便是JS做不到的功能,也推荐采用HTTP接口的方式进行调用。 System.out.println("=======JS Function 执行JAVA类方法======="); String myJavaFun = "var ju=Java.type('TestUtil');function test2(a b){return ju.add(a b)}"; context.eval(Source.create("js" myJavaFun)); Integer i2 = context.getBindings("js").getMember("test2").execute(1 2).asInt(); System.out.println(i2); System.out.println("========JS 接收对象============"); User param = new User("姓名" 44); //User类的属性在这里必须设置成public,只有这样,JS里面才能通过user.name的形式获取到值 String jsPojoParam = "function test5(user){console.log(user);console.log(user.name);console.log(user.age)}"; context.eval(Source.create("js" jsPojoParam)); context.getBindings("js").getMember("test5").execute(param); System.out.println("=========JS文件==========="); //注意此处test.js的地址,在本机或服务器运行时,要根据实际路径进行修改 String jsFile = FileUtils.readFileToString(new File("test.js") "utf-8"); context.eval(Source.create("js" jsFile)); Integer i3 = context.getBindings("js").getMember("test3").execute(1 2).asInt(); System.out.println("执行Function:" i3); User user2 = gson.fromJson( gson.toJson(context.getBindings("js").getMember("test4").execute("张三" 66).as(Map.class)) new TypeToken<User>() { }.getType()); System.out.println("获取返回JSON:" user2.toString()); System.out.println("=========ES 6==========="); context.eval(Source.create("js" "var map=new Map();map.set('name' '张三');map.set('age' 55);map.set('name' '李四');for(var [k v] of map){console.log(k '=' v)}")); context.eval(Source.create("js" "let arr=['苹果' '桔子' '梨'];console.log(...arr);let breakfast=arr.map(fruit => {return '吃' fruit;});console.log(breakfast);")); } }

使用Maven打包后生成的【testgraalvm-jar-with-dependencies.jar】就是包含了所有第三方JAR,可以直接运行的JAR包。

服务器运行

本文的运行环境基于Docker,仅做演示用,如果应用到实际生产环境,还需专业的运维人员进行处理。

本文不描写如何在系统中安装Docker环境,笔者使用的是Docker Desktop。

Image下载

Docker Hub里没有GraalVM的Official Image,社区版放在了https://github.com/graalvm/container。

​拉取:

docker pull ghcr.io/graalvm/graalvm-ce:latest

Container配置

​运行:

-- 容器命名为 mygraalvm docker run --name mygraalvm -it ghcr.io/graalvm/graalvm-ce:latest bash

启动&停止:

docker start mygraalvm docker stop mygraalvm

复制之前编译好的JAR包,​JS文件到Docker:

-- 复制到Docker根目录下,改名为tg.jar docker cp /绝对地址/testgraalvm-jar-with-dependencies.jar 容器ID或name:/tg.jar docker cp /绝对地址/test.js 容器ID或name:/test.js

在运行的容器中执行命令:

docker exec -it mygraalvm bash

此时执行ls,应该能看到前面上传的tg.jar。

修改Docker时区、中文字符集:

在本文中,仅是临时修改,供开发人员暂时测试使用。

-- 进入容器后执行 export TZ=CST-8 export LANG=C.UTF-8

JAR运行

执行​JAR:

-- 前文的代码,没有设置包名 java -cp tg.jar 实际包名.TestGraalVM

运行结果​:

gvm安装要多少时间(GraalVM安装配置与简单使用)(5)

猜您喜欢: