Scripting Java(一):Java中执行脚本
Many implementations of scripting and dynamically typed languages generate Java bytecodes so that programs can be run on the Java Platform, just as are actual Java programs. Implementing a language in this way (or as a Java interpreter class for the scripting language) provides all the advantages of the Java platform: scripting implementations can take advantage of the Java platform‘s binary portability, security, and high performance bytecode execution.
上面这段话可以很好的回答为什么会有这么多基于Java平台的脚本语言。anyway,今天我们只是来看看怎么在我们的Java代码中执行脚本语言。下面使用两种语言来演示,Groovy 2.4.0和Scala 2.11.5。
有两种方式可以在Java中执行脚本,
Java SE includes JSR 223: Scripting for the Java Platform API. This is a framework by which Java applications can "host" script engines. The scripting framework supports third-party script engines using the JAR service discovery mechanism. It is possible to drop any JSR-223 compliant script engine in the CLASSPATH and access the same from your Java applications (much like JDBC drivers, JNDI implementations are accessed).
所以我们既可以使用JSR223的API来执行脚本,当然也可以直接使用脚本语言提供的API来执行了。下面分别来看看。
JSR-223
通过ScriptEngineManager#getEngineByName这个API我们可以拿到实现了JSR-223的脚本引擎,那么我们怎么知道传什么参数才能拿到Groovy跟Scala的引擎呢?冲进去看看代码就知道了,
public ScriptEngine getEngineByName(String shortName) { if (shortName == null) throw new NullPointerException(); //look for registered name first Object obj; if (null != (obj = nameAssociations.get(shortName))) { ScriptEngineFactory spi = (ScriptEngineFactory)obj; try { ScriptEngine engine = spi.getScriptEngine(); engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); return engine; } catch (Exception exp) { if (DEBUG) exp.printStackTrace(); } } for (ScriptEngineFactory spi : engineSpis) { List<String> names = null; try { names = spi.getNames(); } catch (Exception exp) { if (DEBUG) exp.printStackTrace(); } if (names != null) { for (String name : names) { if (shortName.equals(name)) { try { ScriptEngine engine = spi.getScriptEngine(); engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); return engine; } catch (Exception exp) { if (DEBUG) exp.printStackTrace(); } } } } } return null; }
/** * Registers a <code>ScriptEngineFactory</code> to handle a language * name. Overrides any such association found using the Discovery mechanism. * @param name The name to be associated with the <code>ScriptEngineFactory</code>. * @param factory The class to associate with the given name. * @throws NullPointerException if any of the parameters is null. */ public void registerEngineName(String name, ScriptEngineFactory factory) { if (name == null || factory == null) throw new NullPointerException(); nameAssociations.put(name, factory); }
所以可以看到有两种方式可以找到ScriptEngine,
- 如果已经通过调用ScriptEngineManager#registerEngineName注册了名字,那么需要知道注册的是啥名字;
- 如果没有,那就需要知道具体的ScriptEngineFactory实现的getNames方法返回了啥;
ScriptEngineFactory是通过ServiceLoader机制来进行加载的,看下Groovy跟Scala各自的实现都是啥,
javax.script.ScriptEngineFactory文件内容分别是,
org.codehaus.groovy.jsr223.GroovyScriptEngineFactory
scala.tools.nsc.interpreter.IMain$Factory
没有找到有调用registerEngineName方法,那就看下各自的getNames方法吧,GroovyScriptEngineFactory的,
private static final String SHORT_NAME = "groovy"; private static final String LANGUAGE_NAME = "Groovy";
static { List<String> n = new ArrayList<String>(2); n.add(SHORT_NAME); n.add(LANGUAGE_NAME); NAMES = Collections.unmodifiableList(n); n = new ArrayList<String>(1); n.add("groovy"); EXTENSIONS = Collections.unmodifiableList(n); n = new ArrayList<String>(1); n.add("application/x-groovy"); MIME_TYPES = Collections.unmodifiableList(n); }
所以可以通过"groovy"或者"Groovy"来拿到Groovy的ScriptEngine。看下IMain,
@BeanProperty val names: JList[String] = asJavaList("scala")
alright,那就是通过"scala"来拿到了。代码在后面,两种方式一起上。
Shell
通过groovy.lang.GroovyShell和IMain都可以直接执行脚本,这两个就是上面我们说的脚本语言提供的API。
我们可以在脚本语言提供的shell环境执行试试,
$ sudo groovy/bin/groovysh [sudo] password for blues: Feb 3, 2015 12:36:43 AM org.codehaus.groovy.runtime.m12n.MetaInfExtensionModule newModule WARNING: Module [groovy-nio] - Unable to load extension class [org.codehaus.groovy.runtime.NioGroovyMethods] Groovy Shell (2.4.0, JVM: 1.6.0_33) Type ':help' or ':h' for help. ----------------------------------------------------------------------------- groovy:000> import groovy.lang.GroovyShell ===> groovy.lang.GroovyShell groovy:000> groovySh = new GroovyShell() ===> groovy.lang.GroovyShell@1e74f8b groovy:000> groovySh.evaluate("import java.util.Random; random = new Random(); random.nextInt();") ===> -1378717507
$ scala Welcome to Scala version 2.11.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_51). Type in expressions to have them evaluated. Type :help for more information. scala> import scala.tools.nsc.interpreter._ import scala.tools.nsc.interpreter._ scala> val iMain = new IMain() iMain: scala.tools.nsc.interpreter.IMain = scala.tools.nsc.interpreter.IMain@65adfc7b scala> iMain.eval("import java.util.Random; val random = new Random(); random.nextInt();") res0: Object = 1320282527
都没有问题。
最后,回到最开始的问题,怎么在Java代码中执行脚本?下面就把两种方式的代码一起贴上,
import groovy.lang.GroovyShell; import scala.tools.nsc.interpreter.IMain; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; public class Main { public static void main(String[] args) throws Exception { ScriptEngineManager sem = new ScriptEngineManager(); String script_groovy = "import java.util.Random; random = new Random(); random.nextInt();"; ScriptEngine se_groovy = sem.getEngineByName("groovy"); System.out.println(se_groovy.eval(script_groovy)); GroovyShell groovyShell = new GroovyShell(); System.out.println(groovyShell.evaluate(script_groovy)); String script_scala = "import java.util.Random; val random = new Random(); random.nextInt();"; ScriptEngine se_scala = sem.getEngineByName("scala"); System.out.println(se_scala.eval(script_scala)); IMain iMain = new IMain(); System.out.println(iMain.eval(script_scala)); } }
1424725324 1691255297 459327395 1109624277
本来想玩下JDK自带的JSR223实现,也就是Nashorn,which is an implementation of the ECMAScript Edition 5.1 Language Specification,但是要到JDK8才有得玩,改天吧^_^ 轻拍。
参考资料
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。