前言
現代許多流行的應用程序,越來越多的使用了腳本引擎,最典型的有 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應用 程序中是多麼的容易。
本文配套源碼