ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java深入解析篇六十四之脚本引擎(javax.script)详解

Java深入解析篇六十四之脚本引擎(javax.script)详解 Java 脚本引擎javax.script详解全面覆盖 javax.script 脚本引擎框架从 ScriptEngine 到多语言互操作适用版本JDK 6JSR-223引入JDK 8-14 含 NashornJDK 15 需外部引擎一、脚本引擎概述1.1 什么是脚本引擎javax.script 是 JDK 62006JSR-223引入的脚本引擎框架允许 Java 程序在运行时嵌入和执行脚本语言JavaScript、Groovy、Python 等实现动态逻辑、规则引擎和配置驱动。1.2 核心价值价值说明动态逻辑无需重新编译即可修改业务规则多语言互操作Java 调用脚本脚本调用 Java规则引擎业务规则用脚本表达运行时求值配置驱动用脚本语言编写配置和转换逻辑1.3 引擎演进历史版本内置引擎说明JDK 6RhinoJavaScript首个内置引擎JDK 8NashornJavaScript替代 Rhino性能更好JDK 11Nashorn废弃标记为废弃JDK 15无移除 Nashorn需外部引擎GraalJSJDK 17无GraalJS 作为推荐替代二、核心 API2.1 架构全景// javax.script 核心类关系// ScriptEngineManager → ScriptEngine → Bindings// → ScriptContext// → Compilable / Invocable2.2 ScriptEngineManager — 引擎工厂importjavax.script.*;publicclassScriptEngineDemo{publicstaticvoidmain(String[]args)throwsException{// 创建引擎管理器ScriptEngineManagermanagernewScriptEngineManager();// 列出所有可用引擎for(ScriptEngineFactoryfactory:manager.getEngineFactories()){System.out.println(引擎: factory.getEngineName());System.out.println(语言: factory.getLanguageName());System.out.println(版本: factory.getLanguageVersion());System.out.println(别名: factory.getNames());System.out.println(---);}// 按名称获取引擎ScriptEnginejsEnginemanager.getEngineByName(js);ScriptEnginegroovyEnginemanager.getEngineByName(groovy);// 按扩展名获取引擎ScriptEnginejsByExtmanager.getEngineByExtension(js);// 按 MIME 类型获取引擎ScriptEnginejsByMimemanager.getEngineByMimeType(application/javascript);}}2.3 ScriptEngine — 执行脚本importjavax.script.*;publicclassEvalDemo{publicstaticvoidmain(String[]args)throwsException{ScriptEngineManagermanagernewScriptEngineManager();ScriptEngineenginemanager.getEngineByName(js);// 或 graal.jsif(enginenull){System.out.println(未找到 JavaScript 引擎请添加 GraalJS 依赖);return;}// 1. 简单求值Objectresultengine.eval(1 2 * 3);System.out.println(结果: result);// 7// 2. 执行多行脚本engine.eval( function greet(name) { return Hello, name !; } var message greet(Java); );// 3. 获取脚本中的变量Objectmessageengine.get(message);System.out.println(message);// Hello, Java!// 4. 向脚本传递变量engine.put(userName,Alice);engine.put(score,95);Objectgreetingengine.eval(欢迎 userName 得分: score);System.out.println(greeting);// 欢迎 Alice得分: 95}}2.4 Bindings — 变量绑定importjavax.script.*;publicclassBindingsDemo{publicstaticvoidmain(String[]args)throwsException{ScriptEngineManagermanagernewScriptEngineManager();ScriptEngineenginemanager.getEngineByName(js);// 创建独立的绑定Bindingsbindingsengine.createBindings();bindings.put(x,10);bindings.put(y,20);// 使用独立绑定执行Objectresultengine.eval(x y,bindings);System.out.println(x y result);// 30// 引擎全局绑定BindingsglobalScopeengine.getBindings(ScriptContext.GLOBAL_SCOPE);globalScope.put(pi,3.14159);// 引擎局部绑定BindingsengineScopeengine.getBindings(ScriptContext.ENGINE_SCOPE);engineScope.put(e,2.71828);engine.eval(print(pi pi));// pi 3.14159engine.eval(print(e e));// e 2.71828}}2.5 Compilable — 编译优化importjavax.script.*;publicclassCompilableDemo{publicstaticvoidmain(String[]args)throwsException{ScriptEngineManagermanagernewScriptEngineManager();ScriptEngineenginemanager.getEngineByName(js);// 检查是否支持编译if(engineinstanceofCompilable){Compilablecompilable(Compilable)engine;// 编译脚本只编译一次Stringscript function fibonacci(n) { if (n 1) return n; return fibonacci(n - 1) fibonacci(n - 2); } fibonacci(input); ;CompiledScriptcompiledcompilable.compile(script);// 多次执行复用编译结果for(inti0;i5;i){Bindingsbindingsengine.createBindings();bindings.put(input,i*5);Objectresultcompiled.eval(bindings);System.out.println(fib((i*5)) result);}}}}2.6 Invocable — 调用脚本函数importjavax.script.*;publicclassInvocableDemo{publicstaticvoidmain(String[]args)throwsException{ScriptEngineManagermanagernewScriptEngineManager();ScriptEngineenginemanager.getEngineByName(js);// 定义脚本函数engine.eval( function add(a, b) { return a b; } function multiply(a, b) { return a * b; } function formatUser(name, age) { return name ( age 岁); } );// 检查是否支持调用if(engineinstanceofInvocable){Invocableinvocable(Invocable)engine;// 调用脚本函数Objectsuminvocable.invokeFunction(add,3,5);System.out.println(3 5 sum);// 8Objectproductinvocable.invokeFunction(multiply,4,6);System.out.println(4 * 6 product);// 24Objectuserinvocable.invokeFunction(formatUser,Alice,25);System.out.println(user);// Alice (25岁)// 获取脚本对象作为 Java 接口实现engine.eval( var calculator { add: function(a, b) { return a b; }, subtract: function(a, b) { return a - b; } }; );ObjectcalcObjengine.get(calculator);Calculatorcalcinvocable.getInterface(calcObj,Calculator.class);System.out.println(10 3 calc.add(10,3));System.out.println(10 - 3 calc.subtract(10,3));}}interfaceCalculator{Objectadd(inta,intb);Objectsubtract(inta,intb);}}三、Java 与脚本互操作3.1 脚本中访问 Java 类// JavaScript 中访问 JavaNashorn/GraalJS 语法engine.eval( // 访问 Java 类 var ArrayList Java.type(java.util.ArrayList); var list new ArrayList(); list.add(Hello); list.add(World); print(列表大小: list.size()); // 访问 Java 静态方法 var Math Java.type(java.lang.Math); print(PI Math.PI); print(sqrt(16) Math.sqrt(16)); // 访问 Java 系统属性 var System Java.type(java.lang.System); print(Java版本: System.getProperty(java.version)); );3.2 脚本中操作 Java 对象// 传递 Java 对象到脚本MapString,ObjectconfignewHashMap();config.put(host,localhost);config.put(port,8080);engine.put(config,config);engine.eval( print(主机: config.get(host)); print(端口: config.get(port)); config.put(debug, true); );System.out.println(debug config.get(debug));// true四、引擎发现机制4.1 ServiceLoader 注册脚本引擎通过ServiceLoader机制发现需要在 JAR 中包含META-INF/services/javax.script.ScriptEngineFactory文件内容为引擎工厂类的全限定名com.oracle.truffle.js.scriptengine.GraalJSEngineFactory4.2 手动注册引擎// 手动注册引擎工厂ScriptEngineManagermanagernewScriptEngineManager();manager.registerEngineName(my-engine,newMyScriptEngineFactory());manager.registerEngineExtension(my,newMyScriptEngineFactory());manager.registerEngineMimeType(text/x-my-lang,newMyScriptEngineFactory());五、实际应用场景5.1 规则引擎importjavax.script.*;publicclassRuleEngine{privatefinalScriptEngineengine;publicRuleEngine(){ScriptEngineManagermanagernewScriptEngineManager();this.enginemanager.getEngineByName(js);}publicbooleanevaluate(Stringrule,MapString,Objectcontext)throwsException{Bindingsbindingsengine.createBindings();bindings.putAll(context);Objectresultengine.eval(rule,bindings);returnBoolean.TRUE.equals(result);}publicstaticvoidmain(String[]args)throwsException{RuleEngineruleEnginenewRuleEngine();// 业务规则订单金额 1000 且 用户等级 3 则免运费StringruleorderAmount 1000 userLevel 3;MapString,Objectcontext1Map.of(orderAmount,1500,userLevel,5);MapString,Objectcontext2Map.of(orderAmount,500,userLevel,5);System.out.println(订单1免运费: ruleEngine.evaluate(rule,context1));// trueSystem.out.println(订单2免运费: ruleEngine.evaluate(rule,context2));// false}}5.2 动态配置转换publicclassConfigTransformer{publicstaticObjecttransform(Stringscript,MapString,Objectinput)throwsException{ScriptEngineManagermanagernewScriptEngineManager();ScriptEngineenginemanager.getEngineByName(js);Bindingsbindingsengine.createBindings();bindings.put(input,input);returnengine.eval(script,bindings);}publicstaticvoidmain(String[]args)throwsException{MapString,ObjectrawDataMap.of(firstName,John,lastName,Doe,birthYear,1990);StringtransformScript var result {}; result.fullName input.firstName input.lastName; result.age new Date().getFullYear() - input.birthYear; result; ;Objectresulttransform(transformScript,rawData);System.out.println(result);}}六、GraalJS 使用JDK 156.1 Maven 依赖dependenciesdependencygroupIdorg.graalvm.js/groupIdartifactIdjs/artifactIdversion23.0.2/version/dependencydependencygroupIdorg.graalvm.js/groupIdartifactIdjs-scriptengine/artifactIdversion23.0.2/version/dependency/dependencies6.2 使用 GraalJS// GraalJS 完全兼容 javax.script APIScriptEngineManagermanagernewScriptEngineManager();ScriptEngineenginemanager.getEngineByName(graal.js);// 或者ScriptEngineenginemanager.getEngineByName(js);// 使用方式与上述完全相同Objectresultengine.eval(1 1);七、安全注意事项7.1 脚本注入风险// 危险用户输入直接拼入脚本StringuserInput; System.exit(0); ;// 恶意输入engine.eval(var x userInput);// 可能执行恶意代码// 安全通过 Bindings 传参engine.put(userInput,userInput);engine.eval(var x userInput);// 安全作为数据处理7.2 沙箱限制// GraalJS 可以限制脚本能力ContextcontextContext.newBuilder(js).allowIO(false)// 禁止文件操作.allowNativeAccess(false)// 禁止本地调用.allowCreateThread(false)// 禁止创建线程.allowHostAccess(HostAccess.NONE)// 禁止访问 Java.build();总结javax.script 提供了 Java 与脚本语言互操作的标准接口。虽然 JDK 15 移除了内置的 Nashorn 引擎但 GraalJS 作为替代方案完全兼容该 API。脚本引擎在规则引擎、动态配置、模板处理等场景中仍有重要价值。
返回列表