自学内容网 自学内容网

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

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

java中使用js

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

  // 该方式在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环境举例:

     // 使用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();
        }
        <!-- js 支持-->
        <dependency>
            <groupId>org.graalvm.js</groupId>
            <artifactId>js</artifactId>
            <version>21.0.0</version>
        </dependency>

java中使用python

    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();
    }

        <!-- 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脚本
/**
 * @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;
    }
}


@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示例

-- 获取自增ID KEYS[1]对应上文param1
local increment = redis.call("INCR", KEYS[1])
       <!-- 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 孟秋与你

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;
    }

}

     <!--lua支持-->
        <dependency>
            <groupId>org.luaj</groupId>
            <artifactId>luaj-jse</artifactId>
            <version>3.0.1</version>
        </dependency>

java中使用groovy

    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());
    }

       <!-- groovy 支持-->
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy</artifactId>
            <version>3.0.17</version>
        </dependency>

混合使用效果展示

如果将上述脚本混合使用,将会看到一个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的脚本语言


原文地址:https://blog.csdn.net/qq_36268103/article/details/142376698

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!