为Java应用程序加入脚本引擎
内容: 前言 常见的脚本引擎 什么是BeanShell? BeanShell脚本语言简介 在应用程序中嵌入脚本引擎 示例程序 写一个简单的脚本 总结 例程下载 参考资料 关于作者 Java 专区中还有: 教学 工具与产品 代码与组件 所有文章 实用技巧陈先波 ([email protected])
赛微科技
2002 年 11 月
前言
现代许多流行的应用程序,越来越多的使用了脚本引擎,最典型的有Microsoft Office中的VBA等。脚本引擎能提供应用程序极大的可扩展性,也是被许多热忠于二次开发的使用者所乐意看到的。本文主要讲解BeanShell----这样一个Java应用程序脚本引擎,你会了解它的基本特性,及如何将它嵌入到你的应用程序中。你将看到,为自己的应用程序加上脚本引擎是多么容易的一件事情。
常见的脚本引擎
现在网络上流行着许多种脚本语言,如TCL,Perl, JavaScript,Python等,并且有许多脚本语言都有基于Java的解释器,就笔者所知道的有如下几种:
Language Java Implementation JavaScript Rhino Python Jython (formerly JPython) Tcl/Tk Jacl Perl None Java itself BeanShell以上几种脚本都有各自的语法,而其中JavaScript和BeanShell的语法,对于使用者来说更具有亲切感。本文主要讲解BeanShell的的特性及其如何集成到Java应用程序中。
什么是BeanShell?
BeanShell是一个小型的,免费的,可嵌入式的,具有面向对象脚本语言特性的Java代码解释器。它是用Java语言写的。它能执行标准的Java语句和表达式,还自带简单的脚本命令和语法。它把编程对象当成一个简单的方法,这很像Perl和JavaScript.
你可以在写Java测试或调试时使用BeanShell,也可以用它作为你的应用程序的脚本引挚。简而言之,BeanShell可以动态的解释JAVA语言。也就是说BeanShell在许多方面对于Java的用处就像Tcl/Tk对于C的用处一样:BeanShell是可嵌入式的---你可以在运行时从你的应用程序调用BeanShell去动态的执行Java代码或是为你的应用程序提供脚本扩展。相反,你也可以从BeanShell调用你的应用程序及其对象,它可以让JAVA对象和API动态运行。正因为BeanShell是用JAVA写的,所以它可以和你的应用程序运行在同一个JVM空间内,你也可以自由的传递实时对象的引用(References)到脚本代码中并且作为结果返回。
BeanShell脚本语言简介
BeanShell能理解标准的JAVA语句,表达式,和方法宣告。语句和表达式的内容可以是:变量,宣告,赋值,方法调用,循环,条件等。
在Java程序中你必须严格的使用它们,但在BeanShell中,你可以用"宽松类型"(loosely typed)的方式来使用它们。也就是说,你可以在写脚本时偷懒,不进行变量类型的宣告(在原始数据类型和对象都可以)。如果你试着用错变量类型,BeanShell将会给出一个错误。总之BeanShell的脚本是很容易上手的。
这里有一个简单的例子:
foo = "Foo"; four = (2 + 2)*2/2; print( foo + " = " + four ); // print() 是BeanShell的一个脚本命令。 // 循环 for (i=0; i<5; i++) print(i); //显示一个有包含一个按钮的窗口 button = new JButton( "My Button" ); frame = new JFrame( "My Frame" ); frame.getContentPane().add( button, "Center" ); frame.pack(); frame.setVisible(true);
你也可以在Beanshell脚本中宣告和定义自己的方法:
int multiply(int a, int b) { return a*b; } print(multiply(12,14));//打印出168
BeanShell的变量或参数的类型可以不用显示的指定,如下:
int multiply(a, b) { return a*b; } result = multiply(12,14); print(result);//打印出168
在BeanShell中也可以写对象。它与JavaScript很相似,它用一个方法,再内嵌一些内部方法,并且在未尾返回一个this来实现对象的编程:
Employee() { age = 26; sex = "M"; name = "Turbo"; int getAge() { return age; } String getSex() { return sex; } String getName() { return name; } return this; } turbo = Employee(); // 构造一个Employee对象。 print("Name:"+turbo.getName); //打印出employee的名字。
上面的程序片断,相信大家已经对BeanShell的语法有了一个印象,你可以通过参考资料中的链接,找到更详细的BeanShell的脚本语法教程。
在应用程序中嵌入脚本引擎
其实在Java中调用bsh的脚本非常简单,你需要先创建一个Interpreter类的实例.然后就可以用它运行你的脚本代码或者脚本文件。请看下面的一个例子,这个例子来自于BeanShell的文档:
Import bsh.*; //创建一个bsh解释器实例 Interpeter bsh = new Interpreter(); // 表达式求值 bsh.eval("foo=Math.sin(0.5)"); bsh.eval("bar=foo*5; bar=Math.cos(bar);"); bsh.eval("for(i=0; i<10; i) { print(\"hello\"); }"); // 运行语句。 bsh.eval("for(int i=0; i<10; i) { System.out.println(\"hello\"); }"); // 载入脚本文件 bsh.source("myscript.bsh"); // or bsh.eval("source(\"myscript.bsh\")"); // 使用set()和get()方法存取变量值。 bsh.set( "date", new Date() ); Date date = (Date)bsh.get( "date" ); // This would also work: Date date = (Date)bsh.eval( "date" ); bsh.eval("year = date.getYear()"); Integer year = (Integer)bsh.get("year"); // primitives use wrappers // 也可以实现任意的Java Interface.. // 或处理AWT事件。 bsh.eval( "actionPerformed( e ) { print( e ); }"); ActionListener scriptedHandler = (ActionListener)bsh.eval("return (ActionListener)this"); Use the scripted event handler normally... new JButton.addActionListener( script );
示例程序
为了让读者更好的理解,我们在这里以jdk1.4中的一个演示程序,Notepad程序作为基础,向读者讲解BeanShell是如何嵌入到Notepad程序中的。如果你安装了JDK1.4的话,Notepad源程序可以在<JAVA_HOME>\demo\jfc\Notepad目录中找到.
笔者写的一个BeanShell类,用来运行用户的脚本,下面的该程序的完整清单:
import javax.swing.text.*; import javax.swing.*; import java.io.*; import bsh.*; public class BeanShell { private static NameSpace namespace; private static boolean running; private static Notepad notepad; //初始化引擎 public static void initBeanShell(Notepad pad) { notepad = pad; namespace = new NameSpace("Notepad embedded scripting engine."); } //运行一段脚本代码 public static void runScript(String script) { try { StringReader reader = new StringReader(script); runScript( "New script", reader); } catch(Exception ex) { ex.printStackTrace(); System.out.println(ex); return; } } public static void runScript(String scriptInfo, Reader reader) { Interpreter interpreter = new Interpreter(reader,System.out,System.out,true,namespace); try { if(notepad != null) { JTextComponent editor = notepad.getEditor(); interpreter.set("application",notepad); //将应用程序的变量传入解释器, interpreter.set("editor", editor); //这样就可在脚本中使用这个变量参照 running = true; interpreter.eval(reader, namespace, scriptInfo); } } catch(Throwable throwable) { throwable.printStackTrace(); JOptionPane.showMessageDialog(notepad, new Object[] {"Script 错误!", throwable.getMessage()}, "错误",JOptionPane.ERROR_MESSAGE); } finally { running = false; } } public static boolean isMacroRunning() { return running; } }
这个BeanShell提供了三个方法,
void initBeanShell(Notepad),这个方法进行初始化。
void runScript(String script),这个方法可直接运行脚本代码。
void runScript(String scriptInfo, Reader reader),这个方法可从一个Reader流中运行脚本。
而真正核心的执行脚本的代码在void runScript(String scriptInfo, Reader reader)方法中,大家注意,代码中先创建了一个BeanShell的解释器实例,
Interpreter interpreter = new Interpreter(reader,System.out,System.out,true,namespace);
然后将Notepad程序的实例变量传入解释器,这样就可直接在脚本中引用Notedpad的实例了,
JTextComponent editor = notepad.getEditor(); interpreter.set("application",notepad); //将应用程序的变量传入解释器, interpreter.set("editor", editor); //这样就可在脚本中使用这个变量参照
接下来看看如何把BeanShell嵌入到Notepad程序中,笔者写了几个类, Main,用来初始化,并在Notepad程序中加入一个"Script Editor"的菜单 ScriptEditor,简单的脚本编辑器,用来编辑或载入脚本,运行脚本程序。 ScriptEditorAction,启动Script Editor的Action.
下面是Main.class的主要代码,
Notepad notepad = new Notepad(); JMenu editMenu = new JMenu("Script Editor"); JMenuItem editItem = new JMenuItem(); editMenu.add(editItem); editItem.setAction(new ScriptEditorAction(notepad)); notepad.getMenubar().add(editMenu);
以上程序取得Notepad程序的Menu Bar,并添加一个Script Editor菜单到其中,这样,就可不用修改Notepad程序,并在其中加入启动Script Editor的菜单。
下图是程序运行中的画面:
这个程序的完整代码,请在文章未尾的参考资料中去下载。
写一个简单的脚本
现在我们写一个简单的脚本代码,用来统计某个单词在Notepad文章中的出现次数,下面列出这个脚本的代码:
/** * 字串统计程序。 * @author: turbo chen */ //字串统计函数 counter(s) { str = editor.getText(); str = str.toLowerCase(); slen = s.length(); s = s.toLowerCase(); count = 0; from = 0; while ( (from=str.indexOf(s,from))>-1 ) { count = count + 1; from = from + slen; } return count; } // application 是经由BeanShell传入的应用程序对象。 target = JOptionPane.showInputDialog(application,"Please input the count target:"); if ( target != null ) { count = counter(target); JOptionPane.showMessageDialog(application,"Found "+count+" targets."); }
将上面的脚本输入到"Script Editor"视窗中,运行它。看看程序运行的画面:
(查找"of"这个单词在文章中出现的次数,)
(找到13个目标。)
总结
本文讲解了BeanShell脚本引擎如何在现有的应用程序中应用,也让读者了解到BeanShell脚本的基本特性与脚本语法,让大家看到嵌入脚本引擎到Java应用程序中是多么的容易。
例程下载
notepad.rar包括了本文示例中的所有源码。
参考资料
这儿有一个作者翻译的BeanShell的中文简介: http://www.csdn.net/develop/read_article.asp?id=15090 BeanShell的完整在线教程: http://www.beanshell.org/manual/contents.html 你也可以直接到BeanShell的首页下载最新的BeanShell引擎: http://www.beanshell.org/ 如果想了解BeanShell与其它几种脚本语言之间的评测文章,可以点击下面的链接: http://tech.enet.com.cn/document/20020521/20020521154519011.shtml本文地址:http://com.8s8s.com/it/it17938.htm