Java中运⾏动态脚本
这⾥主要总结Java中集成Groovy的应⽤。
Groovy可以与Java完美集成来扩展我们的应⽤,⽐如替代Java+jexl实现算式表达式计算或其它功能。在Ofbiz中也集成了Groovy来执⾏⼀些查询功能,并且是开始更多的使⽤Groovy⽽不是原有的bsh。这⾥仅仅初步总结我们在Java项⽬中如何来应⽤Groovy扩展我们的应⽤。
1.使⽤GroovyShell计算表达式
使⽤Binding对象将变量传⼊表达式,并通过GroovyShell返回表达式的计算结果。如下例:
public class GroovyShellExample {
public static void main(String args[]) {
Binding binding = new Binding();
binding.setVariable("x", 10);
binding.setVariable("language", "Groovy");
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("println \"Welcome to $language\"; y = x * 2; z = x * 3; return x ");
}
}
运⾏结果如下:
Welcome to Groovy
10, true
20, true
30, true
2.使⽤GroovyScriptEngine脚本引擎加载Groovy脚本
GroovyScriptEngine从指定的位置(⽂件系统,URL,数据库等等)加载Groovy脚本,并且随着脚本变化可重新加载它们。和GroovyShell⼀样,GroovyScriptEngine也可以传进变量值返回脚本的计算结果。这样我们可以把⼀些可⽤的计算公式或计算条件写⼊Groovy脚本中来执⾏应⽤计算。当这些公式或计算条件变更时,我们可更⽅便地进⾏更改计算。如:
public class GroovyScriptEngineExample {
public static void main(String args[]) {
try {
String[] roots = new  String[]{".\\src\\sample\\"} ;//定义Groovy脚本引擎的根路径
GroovyScriptEngine engine = new GroovyScriptEngine(roots);
Binding binding = new Binding();
binding.setVariable("language", "Groovy");
Object value = engine.run("vy", binding);
assert value.equals("The End");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//vy
println "Welcome to $language"
return "The End"
运⾏结果如下:
Welcome to Groovy
3.使⽤GroovyClassLoader动态地载⼊Groovy的类
下例现⽰如何使⽤GroovyClassLoader加载Groovy类并且调⽤该类的⼀个⽅法
package sample;
public class GroovyClassLoaderExample {
public static void main(String args[]) {
try {
GroovyClassLoader loader = new GroovyClassLoader();
Class fileCreator = loader.parseClass(new File("vy"));
GroovyObject object = (GroovyObject) wInstance();
object.invokeMethod("createFile", "C:\\temp\\");
} catch (Exception e) {
e.printStackTrace();
}
}
}
package sample;
class GroovySimpleFileCreator {
public createFile(String fileName){
File file = new File(fileName);
}
}
使⽤GroovyClassLoader另⼀种情景便是:存在⼀个Java接⼝和⼀个实现该Java接⼝的Groovy类。此时,可以通过GroovyClassLoader加载Groovy实现类到应⽤中,这样就可以直接调⽤该接⼝的⽅法。
接⼝定义如下:
package sample;
public interface IFoo {
Object run(Object foo);
}
package sample;
public class InvokeGroovy {
public static void main(String[] args) {
ClassLoader cl = new InvokeGroovy().getClass().getClassLoader();
GroovyClassLoader groovyCl = new GroovyClassLoader(cl);
try {
//从⽂件中读取,将实现IFoo接⼝的groovy类写在⼀个groovy⽂件中
//Class groovyClass = groovyCl.parseClass(new File("./src/vy"));
//直接使⽤Groovy字符串,也可以获得正确结果
Class groovyClass = groovyCl.parseClass("package sample; \r\n class Foo implements IFoo {public Object run(Object foo) {return 2+2>1}}");//这个返回true
IFoo foo = (IFoo) wInstance();
System.out.println(foo.run(new Integer(2)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.使⽤JAVA脚本API
Java SE 6 引⼊了对 Java Specification Request(JSR)223 的⽀持,JSR 223 旨在定义⼀个统⼀的规范,使得 Java 应⽤程序可以通过⼀套固定的接⼝与各种脚本引擎交互,从⽽达到在 Java 平台上调⽤各种脚本语⾔的⽬的。每⼀个脚本引擎就是⼀个脚本解释器,负责运⾏脚本,获取运⾏结果。ScriptEngine 接⼝提供了许多 eval 函数的变体⽤来运⾏脚本,这个函数的功能就是获取脚本输⼊,运⾏脚本,最后返回输出。
下例显⽰了⼀个使⽤JAVA脚本API运⾏Groovy的例⼦:
public class GroovyJSR223Example {
public static void main(String args[]) {
try {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = EngineByName("groovy");
String HelloLanguage = "def hello(language) {return \"Hello $language\"}";
engine.eval(HelloLanguage);
Invocable inv = (Invocable) engine;
Object[] params = {new String("Groovy")};
Object result = inv.invokeFunction("hello", params);
assert result.equals("Hello Groovy");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Java脚本API除了可以运⾏Groovy外,还可以运⾏其它脚本程序,如JavaScript、BSH等。
PipedReader pr = new PipedReader();
PipedWriter pw = new PipedWriter(pr);
PrintWriter writer = new PrintWriter(pw);
String script = "println('Hello from JavaScript')";
engine.eval(script);
BufferedReader br =new BufferedReader(pr);
System.out.adLine());
}
}
共有三个级别的地⽅可以存取属性,分别是 ScriptEngineManager 中的 Bindings,ScriptEngine 实例对应的 ScriptContext 中含有的Bindings,以及调⽤ eval 函数时传⼊的 Bingdings。离函数调⽤越近,其作⽤域越⼩,优先级越⾼。下例中可以看出各个属性的存取优先级:
public class ScopeTest {
public static void main(String[] args) throws Exception {
String script="println(greeting)";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = EngineByName("javascript");
//Attribute from ScriptEngineManager
manager.put("greeting", "Hello from ScriptEngineManager");
engine.eval(script);
//Attribute from ScriptEngine
engine.put("greeting", "Hello from ScriptEngine");
engine.eval(script);
//Attribute from eval method
ScriptContext context = new SimpleScriptContext();
context.setAttribute("greeting", "Hello from eval method", ScriptContext.ENGINE_SCOPE);
engine.eval(script,context);
}
}
在 Java 脚本 API 中还有两个脚本引擎可以选择是否实现的接⼝,这个两个接⼝不是强制要求实现的,即并⾮所有的脚本引擎都能⽀持这两个函数:
Invocable 接⼝:允许 Java 平台调⽤脚本程序中的函数或⽅法。Invocable 接⼝还允许 Java 应⽤程序从这些函数中直接返回⼀个接⼝,通过这个接⼝实例来调⽤脚本中的函数或⽅法,从⽽我们可以从脚本中动态的⽣成 Java 应⽤中需要的接⼝对象。
Compilable 接⼝:允许 Java 平台编译脚本程序,供多次调⽤。
下例调⽤脚本中的函数:
public class CompilableTest {
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
String script = " function greeting(message){println (message);}";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = EngineByName("javascript");
engine.eval(script);
if (engine instanceof Invocable) {
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("greeting", "hi");
// It may through NoSuchMethodException
try {
invocable.invokeFunction("nogreeing");
} catch (NoSuchMethodException e) {
/
/ expected
}
}
}
}
下例演⽰了如何使⽤ Compiable 接⼝来调⽤脚本:
public class CompilableTest {
public static void main(String[] args) throws ScriptException {
String script = " println (greeting); greeting= 'Good Afternoon!' ";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = EngineByName("javascript");
engine.put("greeting", "Good Morning!");
if (engine instanceof Compilable) {
Compilable compilable = (Compilable) engine;
CompiledScript compiledScript = compilablepile(script);
compiledScript.eval();
compiledScript.eval();
}
}shell脚本返回执行结果
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。