【script】java武魂技展示:在java中使用不同的脚本语言 一文体现java生态的强大

我们经常听到java强大在于它的生态,对于生态的理解我们一般可能想到的是spring家族、微服务那一套中间件;其实java生态的强大也体现在它能使用各种脚本语言,博主最近在项目中考虑使用脚本语言以达到动态效果,因此顺带例举了常用的脚本语言方式

文章目录

java中使用js

在旧版本的jdk(8-14)中 默认是带有js引擎的,使用较为通用的方式即可:

java 复制代码
  		// 该方式在jdk15已经不可用 被移除了 Nashorn , 在jdk15之前 默认是通过Nashorn来作为JavaScriptEngine的
        ScriptEngineManager MANAGER = new ScriptEngineManager();
        // JavaScriptEngine  获取一个JavaScript引擎 (脚本语言本质是实现ScriptEngine接口)
        ScriptEngine engine = MANAGER.getEngineByName("js");
        // 定义JavaScript代码
         script = "var a = 1; var b = 2; a + b;";

        try {
            // 执行JavaScript代码
            Object result = engine.eval(script);
            // jdk >=15 engine is null
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }

本文不再介绍低版本jdk使用方式,以下所有代码皆基于jdk21环境举例:

java 复制代码
     // 使用js方式
        try (Context context = Context.create()) {
            Value result = context.eval("js", "var a = 1; var b = 2; a + b;");
            System.out.println(result.asInt());
            // 3
            return result.toString();
        }
xml 复制代码
        <!-- js 支持-->
        <dependency>
            <groupId>org.graalvm.js</groupId>
            <artifactId>js</artifactId>
            <version>21.0.0</version>
        </dependency>

java中使用python

java 复制代码
    public String test(String script) {
        // Jython 只支持 Python 2 语法,且无法调用用 C 扩展编写的 Python 模块(例如一些涉及原生代码的库)。
        // 其它方式不受python版本限制的方式:  1.ProcessBuilder  需要安装了python 并配置环境变量
        // 2. GraalVM 虚拟机(支持多语言) 这种方式需要你使用 GraalVM 作为运行环境,并安装 Python 语言支持
        PythonInterpreter interpreter = new PythonInterpreter();
        interpreter.exec("print('原创作者csdn:孟秋与你')");

        // 执行带参数的 Python 脚本
        interpreter.set("javaVar", new PyInteger(42));
        interpreter.exec("pythonVar = javaVar * 2");
        PyObject result = interpreter.get("pythonVar");
       return result.toString();
    }
xml 复制代码
        <!-- python 2支持-->
        <dependency>
            <groupId>org.python</groupId>
            <artifactId>jython-standalone</artifactId>
            <version>2.7.2</version>
        </dependency>

java中使用lua

我们经常在redis中使用lua脚本 达到分布式锁的效果 例如redisson组件也是通过lua脚本写的

  1. redis使用lua脚本
java 复制代码
/**
 * @author csdn:孟秋与你 /github:qiuhuanhen
 */
@Configuration
public class LuaScriptConfig {

    @Bean
    public RedisScript<String> script() {
        DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
        // resource目录下创建的scripts文件夹
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/xxx.lua")));
        redisScript.setResultType(String.class);
        return redisScript;
    }

    @Bean
    public RedisTemplate<String, Object> luaRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 设置 key 和 value 的序列化方式为 String
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(stringRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
java 复制代码
@RequestMapping("/redis")
@RestController
public class RedisIdController {

    @Autowired
    private LuaRedisTemplateluaRedisTemplate;
    
    @Autowired
    private RedisScript<String> script;

    @GetMapping
    public String test() {
		// 取决你的脚本需要几个传参
        return luaRedisTemplate.execute(script, java.util.List.of("param1", "param2"....));
    }
}

lua示例

lua 复制代码
-- 获取自增ID KEYS[1]对应上文param1
local increment = redis.call("INCR", KEYS[1])
xml 复制代码
       <!-- Spring Data Redis version和springboot版本一致 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  
  1. 纯java中使用lua脚本

使用lua脚本相比其它脚本语言 有一个优势在于权限可控,可以通过控制load的模块,极大的限制lua脚本能做的事情;换句话说,当我们把脚本能力开放给维护人员或内部系统接口时,风险也是可控的,不至于被删库跑路。

一般标准下是使用JsePlatform.standardGlobals(); 这个权限还是很危险的,所以我们可以选择基础功能load即可,具体看下面注释:

本文原创作者:csdn 孟秋与你

java 复制代码
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.Bit32Lib;
import org.luaj.vm2.lib.CoroutineLib;
import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.TableLib;
import org.luaj.vm2.lib.jse.JseBaseLib;
import org.luaj.vm2.lib.jse.JseIoLib;
import org.luaj.vm2.lib.jse.JseMathLib;
import org.luaj.vm2.lib.jse.JseOsLib;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.lib.jse.LuajavaLib;
import org.springframework.stereotype.Component;

@Component
public class LuaConfig {
    private Globals globals;

    public LuaConfig() {

        // 使用标准 JSE 环境创建 Lua 环境 (包含标准库,不注入自定义的Java 对象)
//        this.globals = JsePlatform.standardGlobals();

        // 从标准库的源码中,筛选最最基本的功能(降低风险)
        globals = new Globals();
        // 基本函数 print() error()等
        globals.load(new JseBaseLib());
        // 管理 Lua 模块和包,除了JseBaseLib 其它基础模块要用到。允许通过该require()函数加载外部 Lua 模块。
        globals.load(new PackageLib());
        // 提供用于操作整数的按位运算。
        globals.load(new Bit32Lib());
        // 提供操作Lua表(数组、字典)的函数。
        globals.load(new TableLib());
        // 提供字符串操作功能。
        globals.load(new StringLib());
        // 允许使用协同程序(轻量级线程)
//        globals.load(new CoroutineLib());
        // 提供常见的数学函数,如、、sin()等。cos()random()
        globals.load(new JseMathLib());
        // 提供文件输入/输出(I/O)操作的功能。
//        globals.load(new JseIoLib());
        // 提供与操作系统相关的功能,如获取环境变量、执行shell命令等。
//        globals.load(new JseOsLib());
        // 提供从 Lua 脚本与 Java 对象交互的能力 (LuajavaLib 允许 Lua 脚本直接访问 Java 对象、类、甚至 Java 运行时环境)
//        globals.load(new LuajavaLib());

        // 禁用path cpath (加载外部脚本)
//        globals.get("package").set("path", LuaValue.valueOf(""));
//        globals.get("package").set("cpath", LuaValue.valueOf(""));
        // 禁用 require 函数 将外部脚本作为模块导入 (这是个辅助功能,require导入的脚本 依然不能使用globals没load的模块功能 但可能导入外部复杂的脚本)
//        globals.set("require", LuaValue.NIL);
        LoadState.install(globals);
        LuaC.install(globals);
        // 手动注入java对象方式
//        LuaValue controller = CoerceJavaToLua.coerce(new LuaController());
//        this.globals.set("controller", controller);
//        this.globals.set("key", "原创作者 csdn:孟秋与你");
    }

    public LuaValue executeLuaScript(String script) {
        // 加载并执行 Lua 脚本
        LuaValue chunk = globals.load(script);
        return chunk.call();
    }

    public Globals getGlobals() {
        return globals;
    }

}
xml 复制代码
     <!--lua支持-->
        <dependency>
            <groupId>org.luaj</groupId>
            <artifactId>luaj-jse</artifactId>
            <version>3.0.1</version>
        </dependency>

java中使用groovy

java 复制代码
    public String test(Long id) {

        GroovyShell shell = new GroovyShell();
        // 语言特性:自动返回最后一个值  即使是个固定值也会返回
        Script script1 = shell.parse("def temp = binding.variables.get(\"id\") as Long \n \"一个固定值-本文原创作者:csdn孟秋与你\"\n");
        // 一个固定值
        return String.valueOf(script1.run());
    }
xml 复制代码
       <!-- groovy 支持-->
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy</artifactId>
            <version>3.0.17</version>
        </dependency>

混合使用效果展示

如果将上述脚本混合使用,将会看到一个java的武魂组合技:

java 复制代码
/**
 * 不同脚本语言混合演示
 */
@RequestMapping("/test/script")
@RestController
public class ScriptController {
	/**  这是上文lua部分的LuaConfig配置  **/
    @Autowired
    private LuaConfig luaConfig;

    @GetMapping
    public String test() {

        // js
        try (Context context = Context.create()) {
            Value result = context.eval("js", "var a = 333; var b = 333; a + b;");
            System.out.println("js: " + result.asInt());
        }

        // lua
        LuaValue luaValue = luaConfig.executeLuaScript("local res = 666 return tostring(res)");
        System.out.println("lua: " + luaValue.toString());

        // groovy
        Script groovy = new GroovyShell().parse(" def groovy = \"csdn的 孟秋与你 是世界上最好的博主 以及groovy是世界上最好的语言.class\"");
        System.out.println("groovy: " + groovy.run());

        // python
        PythonInterpreter interpreter = new PythonInterpreter();
        interpreter.exec("res = \"**  python\"");
        PyObject result = interpreter.get("res");
        System.out.println("python: " + result.toString());

        return "java";
    }
}

运行展示:

tips: 这回真的 groovy是世界上最好的语言.class,不是玩梗 groovy是生成字节码 运行在jvm的脚本语言

相关推荐
BaiZhuYuan1 分钟前
io流(学习笔记01)--File知识点
java·开发语言
Annuo、3 分钟前
php中根据指定日期获取所在天,周,月,年的开始日期与结束日期
java·服务器·前端
森屿Serien22 分钟前
jvm 内存结构
java·jvm
zheeez27 分钟前
JVM 基本组成
java·jvm
小大力32 分钟前
简单的spring缓存 Cacheable学习
java·redis·缓存
小黑0341 分钟前
Scala第二天
开发语言·后端·scala
OEC小胖胖1 小时前
Spring MVC系统学习(一)——初识Spring MVC框架
java·后端·学习·spring·mvc
AVICCI1 小时前
C++ 基础
开发语言·c++
AutoAutoJack1 小时前
C# 委托(Delegate)二
开发语言·数据结构·算法·架构·c#