Java 脚本编程全解析
1. 更改默认脚本上下文
在 Java 脚本编程中,你可以使用
ScriptEngine
的
getContext()
和
setContext()
方法分别获取和设置默认上下文。以下是具体操作步骤:
1. 创建
ScriptEngineManager
和
ScriptEngine
:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
- 获取默认上下文:
ScriptContext defaultCtx = engine.getContext();
- 创建新的上下文:
ScriptContext ctx = new SimpleScriptContext();
- 配置新上下文,这里可以根据需求进行具体配置。
- 将新上下文设置为引擎的默认上下文:
engine.setContext(ctx);
需要注意的是,为
ScriptEngine
设置新的默认上下文时,不会使用
ScriptEngineManager
的
Bindings
作为全局作用域的
Bindings
。如果你希望新的默认上下文使用
ScriptEngineManager
的
Bindings
,需要显式设置:
ctx.setBindings(manager.getBindings(), ScriptContext.GLOBAL_SCOPE);
engine.setContext(ctx);
1.1 脚本引擎上下文键值含义
| 键 |
ScriptEngine
接口中的常量
| 值的含义 |
|---|---|---|
| “javax.script.language” |
ScriptEngine.LANGUAGE
| 脚本引擎支持的语言名称 |
| “javax.script.language_version” |
ScriptEngine.LANGUAGE_VERSION
| 引擎支持的脚本语言版本 |
| “javax.script.name” |
ScriptEngine.NAME
| 脚本语言的简称 |
2. 将脚本输出发送到文件
你可以自定义脚本执行的输入源、输出目标和错误输出目标。要实现将脚本输出写入文件,需要为用于执行脚本的
ScriptContext
设置适当的读取器和写入器。以下是具体步骤:
1. 创建
FileWriter
:
FileWriter writer = new FileWriter("output.txt");
- 获取引擎的默认上下文:
ScriptContext defaultCtx = engine.getContext();
- 为引擎的默认上下文设置输出写入器:
defaultCtx.setWriter(writer);
以下是完整的示例代码:
// CustomScriptOutput.java
package com.jdojo.script;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class CustomScriptOutput {
public static void main(String[] args) {
// Get the Groovy engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
// Print the absolute path of the output file
File outputFile = new File("output.txt");
System.out.println("Script output will be written to " + outputFile.getAbsolutePath());
try (FileWriter writer = new FileWriter(outputFile)) {
// Set a custom output writer for the engine
ScriptContext defaultCtx = engine.getContext();
defaultCtx.setWriter(writer);
// Execute a script
String script = "println('Hello custom output writer')";
engine.eval(script);
} catch (IOException | ScriptException e) {
e.printStackTrace();
}
}
}
2.1 注意事项
设置
ScriptContext
的自定义输出写入器不会影响 Java 应用程序的标准输出目标。要重定向 Java 应用程序的标准输出,需要使用
System.setOut()
方法。
3. 调用脚本中的过程
脚本语言可能允许创建过程、函数和方法,Java 脚本 API 允许从 Java 应用程序中调用这些过程。但并非所有脚本引擎都支持过程调用,Groovy 引擎支持此功能。调用过程的步骤如下:
1. 检查脚本引擎是否支持过程调用:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
if (engine instanceof Invocable) {
System.out.println("Invoking procedures is supported.");
} else {
System.out.println("Invoking procedures is not supported.");
}
-
将引擎引用转换为
Invocable类型:
Invocable inv = (Invocable) engine;
- 评估包含过程源代码的脚本:
String script = "def add(n1, n2) { n1 + n2 }";
engine.eval(script);
- 调用过程或函数:
Object result = inv.invokeFunction("add", 30, 40);
3.1 调用函数示例
以下是一个完整的调用函数的示例代码:
// InvokeFunction.java
package com.jdojo.script;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class InvokeFunction {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
// Make sure the script engine implements the Invocable interface
if (!(engine instanceof Invocable)) {
System.out.println("Invoking procedures is not supported.");
return;
}
// Cast the engine reference to the Invocable type
Invocable inv = (Invocable) engine;
try {
String script = "def add(n1, n2) { n1 + n2 }";
// Evaluate the script first
engine.eval(script);
// Invoke the add function twice
Object result1 = inv.invokeFunction("add", 30, 40);
System.out.println("Result1 = " + result1);
Object result2 = inv.invokeFunction("add", 10, 20);
System.out.println("Result2 = " + result2);
} catch (ScriptException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
3.2 调用对象方法
对于面向对象或基于对象的脚本语言,你可以使用
Invocable
接口的
invokeMethod()
方法调用对象的方法。以下是调用对象方法的示例代码:
// InvokeMethod.java
package com.jdojo.script;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class InvokeMethod {
public static void main(String[] args) {
// Get the Groovy engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
// Make sure the script engine implements the Invocable interface
if (!(engine instanceof Invocable)) {
System.out.println("Invoking methods is not supported.");
return;
}
// Cast the engine reference to the Invocable type
Invocable inv = (Invocable) engine;
try {
// Declare a global object with an add() method
String script = """
class Calculator {
def add(int n1, int n2){n1 + n2}
}
calculator = new Calculator()
""";
// Evaluate the script first
engine.eval(script);
// Get the calculator object reference created in the script
Object calculator = engine.get("calculator");
// Invoke the add() method on the calculator object
Object result = inv.invokeMethod(calculator, "add", 30, 40);
System.out.println("Result = " + result);
} catch (ScriptException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
3.3 性能提示
使用
Invocable
接口可以重复执行过程、函数和方法。评估包含这些内容的脚本会将中间代码存储在引擎中,从而在重复执行时提高性能。
4. 在脚本中实现 Java 接口
Java 脚本 API 允许在脚本语言中实现 Java 接口。
Invocable
接口的
getInterface()
方法有两个版本,用于获取在脚本中实现的 Java 接口实例:
-
<T> T getInterface(Class<T> cls)
:用于获取方法在脚本中以顶级过程实现的 Java 接口实例。
-
<T> T getInterface(Object obj, Class<T> cls)
:用于获取方法在脚本中以对象实例方法实现的 Java 接口实例。
4.1 顶级过程实现接口示例
以下是使用顶级过程在 Groovy 中实现 Java 接口的示例代码:
// Calculator.java
package com.jdojo.script;
public interface Calculator {
int add (int n1, int n2);
int subtract (int n1, int n2);
}
// UsingInterfaces.java
package com.jdojo.script;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class UsingInterfaces {
public static void main(String[] args) {
// Get the Groovy engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
// Make sure the script engine implements Invocable interface
if (!(engine instanceof Invocable)) {
System.out.println("Interface implementation in script is not supported.");
return;
}
// Cast the engine reference to the Invocable type
Invocable inv = (Invocable) engine;
// Create the script for add() and subtract() functions
String script = """
def add(n1, n2) { n1 + n2 }
def subtract(n1, n2) { n1 - n2 }
""";
try {
// Compile the script that will be stored in the engine
engine.eval(script);
// Get the interface implementation
Calculator calc = inv.getInterface(Calculator.class);
if (calc == null) {
System.err.println("Calculator interface implementation not found.");
return;
}
int result1 = calc.add(15, 10);
System.out.println("add(15, 10) = " + result1);
int result2 = calc.subtract(15, 10);
System.out.println("subtract(15, 10) = " + result2);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
4.2 对象实例方法实现接口示例
以下是使用对象实例方法在 Groovy 中实现 Java 接口的示例代码:
// ScriptObjectImplInterface.java
package com.jdojo.script;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class ScriptObjectImplInterface {
public static void main(String[] args) {
// Get the Groovy engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
// Make sure the engine implements the Invocable interface
if (!(engine instanceof Invocable)) {
System.out.println("Interface implementation in script is not supported.");
return;
}
// Cast the engine reference to the Invocable type
Invocable inv = (Invocable) engine;
String script = """
class GCalculator {
def add(int n1, int n2){n1 + n2}
def subtract(int n1, int n2){n1 + n2}
}
calculator = new GCalculator()
""";
try {
// Compile and store the script in the engine
engine.eval(script);
// Get the reference of the global script object calc
Object calc = engine.get("calculator");
// Get the implementation of the Calculator interface
Calculator calculator = inv.getInterface(calc, Calculator.class);
if (calculator == null) {
System.err.println("Calculator interface implementation not found.");
return;
}
int result1 = calculator.add(15, 10);
System.out.println("add(15, 10) = " + result1);
int result2 = calculator.subtract(15, 10);
System.out.println("subtract(15, 10) = " + result2);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
5. 使用编译后的脚本
部分脚本引擎支持编译脚本并重复执行,这可以提高应用程序的性能。支持脚本编译的引擎必须实现
Compilable
接口,Groovy 引擎支持此功能。使用编译脚本的步骤如下:
1. 检查脚本引擎是否支持编译:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("YOUR_ENGINE_NAME");
if (engine instanceof Compilable) {
System.out.println("Script compilation is supported.");
} else {
System.out.println("Script compilation is not supported.");
}
-
将引擎引用转换为
Compilable类型:
Compilable comp = (Compilable) engine;
- 编译脚本:
CompiledScript cScript = comp.compile(script);
- 执行编译后的脚本:
Object result = cScript.eval();
5.1 编译脚本示例
以下是一个完整的使用编译脚本的示例代码:
// CompilableTest.java
package com.jdojo.script;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class CompilableTest {
public static void main(String[] args) {
// Get the Groovy engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
if (!(engine instanceof Compilable)) {
System.out.println("Script compilation not supported.");
return;
}
// Cast the engine reference to the Compilable type
Compilable comp = (Compilable) engine;
try {
// Compile a script
String script = "println(n1 + n2)";
CompiledScript cScript = comp.compile(script);
// Store n1 and n2 script variables in a Bindings
Bindings scriptParams = engine.createBindings();
scriptParams.put("n1", 2);
scriptParams.put("n2", 3);
cScript.eval(scriptParams);
// Execute the script again with different values for n1 and n2
scriptParams.put("n1", 9);
scriptParams.put("n2", 7);
cScript.eval(scriptParams);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
6. 在脚本语言中使用 Java
脚本语言允许在脚本中使用 Java 类库,不同脚本语言使用 Java 类的语法不同。这里以 Groovy 为例,介绍使用 Java 构造的语法。
6.1 变量声明
在 Groovy 中,使用
def
关键字声明变量。如果省略
def
关键字,变量在脚本范围内可访问,但在脚本内声明的类中不可访问,并且在脚本处理后其值可从 Java 访问。示例代码如下:
// Declare a variable named msg using the def keyword
def msg = "Hello";
// Declare a variable named greeting without using the keyword def.
greeting = "Welcome";
6.2 流程图:调用脚本过程
graph TD;
A[检查脚本引擎是否支持过程调用] --> B{支持?};
B -- 是 --> C[将引擎引用转换为 Invocable 类型];
B -- 否 --> D[结束];
C --> E[评估包含过程源代码的脚本];
E --> F[调用过程或函数];
6.3 流程图:使用编译脚本
graph TD;
A[检查脚本引擎是否支持编译] --> B{支持?};
B -- 是 --> C[将引擎引用转换为 Compilable 类型];
B -- 否 --> D[结束];
C --> E[编译脚本];
E --> F[执行编译后的脚本];
通过以上内容,你可以全面了解 Java 脚本编程的各种功能和操作方法,包括更改默认脚本上下文、将脚本输出发送到文件、调用脚本中的过程、在脚本中实现 Java 接口、使用编译后的脚本以及在脚本语言中使用 Java 等。希望这些内容对你的 Java 脚本编程学习和实践有所帮助。
7. 总结与操作步骤回顾
7.1 操作步骤总结
为了方便大家更好地理解和应用前面介绍的 Java 脚本编程知识,下面对各项操作的步骤进行总结:
| 操作内容 | 操作步骤 |
| ---- | ---- |
| 更改默认脚本上下文 | 1. 创建
ScriptEngineManager
和
ScriptEngine
;2. 获取默认上下文;3. 创建新的上下文;4. 配置新上下文;5. 将新上下文设置为引擎的默认上下文;若希望新上下文使用
ScriptEngineManager
的
Bindings
,需显式设置。 |
| 将脚本输出发送到文件 | 1. 创建
FileWriter
;2. 获取引擎的默认上下文;3. 为引擎的默认上下文设置输出写入器。 |
| 调用脚本中的过程 | 1. 检查脚本引擎是否支持过程调用;2. 将引擎引用转换为
Invocable
类型;3. 评估包含过程源代码的脚本;4. 调用过程或函数。 |
| 在脚本中实现 Java 接口 | - 顶级过程实现:1. 检查脚本引擎是否实现
Invocable
接口;2. 转换引擎引用;3. 编写脚本;4. 评估脚本;5. 获取接口实现。
- 对象实例方法实现:1. 检查脚本引擎是否实现
Invocable
接口;2. 转换引擎引用;3. 编写脚本;4. 评估脚本;5. 获取脚本对象引用;6. 获取接口实现。 |
| 使用编译后的脚本 | 1. 检查脚本引擎是否支持编译;2. 将引擎引用转换为
Compilable
类型;3. 编译脚本;4. 执行编译后的脚本。 |
7.2 代码示例总结
以下是前面介绍的各项操作的代码示例汇总:
更改默认脚本上下文
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
ScriptContext defaultCtx = engine.getContext();
ScriptContext ctx = new SimpleScriptContext();
ctx.setBindings(manager.getBindings(), ScriptContext.GLOBAL_SCOPE);
engine.setContext(ctx);
将脚本输出发送到文件
// CustomScriptOutput.java
package com.jdojo.script;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class CustomScriptOutput {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
File outputFile = new File("output.txt");
System.out.println("Script output will be written to " + outputFile.getAbsolutePath());
try (FileWriter writer = new FileWriter(outputFile)) {
ScriptContext defaultCtx = engine.getContext();
defaultCtx.setWriter(writer);
String script = "println('Hello custom output writer')";
engine.eval(script);
} catch (IOException | ScriptException e) {
e.printStackTrace();
}
}
}
调用脚本中的过程
// InvokeFunction.java
package com.jdojo.script;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class InvokeFunction {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
if (!(engine instanceof Invocable)) {
System.out.println("Invoking procedures is not supported.");
return;
}
Invocable inv = (Invocable) engine;
try {
String script = "def add(n1, n2) { n1 + n2 }";
engine.eval(script);
Object result1 = inv.invokeFunction("add", 30, 40);
System.out.println("Result1 = " + result1);
Object result2 = inv.invokeFunction("add", 10, 20);
System.out.println("Result2 = " + result2);
} catch (ScriptException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
在脚本中实现 Java 接口(顶级过程实现)
// Calculator.java
package com.jdojo.script;
public interface Calculator {
int add (int n1, int n2);
int subtract (int n1, int n2);
}
// UsingInterfaces.java
package com.jdojo.script;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class UsingInterfaces {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
if (!(engine instanceof Invocable)) {
System.out.println("Interface implementation in script is not supported.");
return;
}
Invocable inv = (Invocable) engine;
String script = """
def add(n1, n2) { n1 + n2 }
def subtract(n1, n2) { n1 - n2 }
""";
try {
engine.eval(script);
Calculator calc = inv.getInterface(Calculator.class);
if (calc == null) {
System.err.println("Calculator interface implementation not found.");
return;
}
int result1 = calc.add(15, 10);
System.out.println("add(15, 10) = " + result1);
int result2 = calc.subtract(15, 10);
System.out.println("subtract(15, 10) = " + result2);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
使用编译后的脚本
// CompilableTest.java
package com.jdojo.script;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class CompilableTest {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Groovy");
if (!(engine instanceof Compilable)) {
System.out.println("Script compilation not supported.");
return;
}
Compilable comp = (Compilable) engine;
try {
String script = "println(n1 + n2)";
CompiledScript cScript = comp.compile(script);
Bindings scriptParams = engine.createBindings();
scriptParams.put("n1", 2);
scriptParams.put("n2", 3);
cScript.eval(scriptParams);
scriptParams.put("n1", 9);
scriptParams.put("n2", 7);
cScript.eval(scriptParams);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
7.3 综合流程图:Java 脚本编程操作流程
graph LR;
A[开始] --> B{选择操作};
B -- 更改默认脚本上下文 --> C[执行更改步骤];
B -- 将脚本输出发送到文件 --> D[执行输出步骤];
B -- 调用脚本中的过程 --> E[执行调用步骤];
B -- 在脚本中实现 Java 接口 --> F[选择实现方式];
F -- 顶级过程实现 --> G[执行顶级过程实现步骤];
F -- 对象实例方法实现 --> H[执行对象实例方法实现步骤];
B -- 使用编译后的脚本 --> I[执行编译脚本步骤];
C --> J[结束];
D --> J;
E --> J;
G --> J;
H --> J;
I --> J;
8. 注意事项与常见问题解答
8.1 注意事项
-
上下文设置
:设置新的默认上下文时,不会自动使用
ScriptEngineManager的Bindings作为全局作用域的Bindings,如需使用需显式设置。 -
输出重定向
:设置
ScriptContext的自定义输出写入器不会影响 Java 应用程序的标准输出目标,要重定向标准输出需使用System.setOut()方法。 -
接口实现
:在使用
getInterface()方法获取接口实现时,要确保脚本引擎实现了Invocable接口,否则可能会导致错误。 -
脚本编译
:并非所有脚本引擎都支持脚本编译,支持的引擎必须实现
Compilable接口。
8.2 常见问题解答
问题 1:为什么调用脚本中的过程时提示不支持?
解答:可能是因为所使用的脚本引擎不支持过程调用。在调用前,需要检查脚本引擎是否实现了
Invocable
接口,只有实现了该接口的引擎才支持过程调用。
问题 2:将脚本输出发送到文件后,控制台仍然有输出,如何解决?
解答:设置
ScriptContext
的自定义输出写入器不会影响 Java 应用程序的标准输出目标。若要重定向标准输出,需要使用
System.setOut()
方法。
问题 3:在脚本中实现 Java 接口时,获取的接口实现为
null
怎么办?
解答:这可能是因为脚本编写有误,或者脚本引擎在解析脚本时出现问题。需要检查脚本内容是否正确实现了接口的方法,以及脚本引擎是否正确评估了脚本。
9. 总结与展望
9.1 总结
通过前面的介绍,我们详细了解了 Java 脚本编程的多个方面,包括更改默认脚本上下文、将脚本输出发送到文件、调用脚本中的过程、在脚本中实现 Java 接口、使用编译后的脚本以及在脚本语言中使用 Java 等功能。每个功能都有其特定的操作步骤和注意事项,并且通过代码示例和流程图进行了直观的展示。
9.2 展望
Java 脚本编程为 Java 应用程序提供了更加灵活和动态的功能扩展方式。随着技术的不断发展,脚本引擎的性能和功能也会不断提升。未来,我们可以期待在更多的应用场景中使用 Java 脚本编程,例如在自动化测试、动态配置、规则引擎等领域。同时,对于脚本编程的安全性和性能优化也将成为研究的重点方向。希望大家能够通过本文的学习,掌握 Java 脚本编程的基本技能,并在实际项目中灵活应用。
通过以上内容,我们对 Java 脚本编程有了全面而深入的了解。在实际应用中,大家可以根据具体需求选择合适的操作方法,并注意相关的注意事项,以确保程序的正确性和性能。希望这些知识能够帮助大家在 Java 脚本编程的道路上取得更好的成果。
超级会员免费看
1750

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



