BeanShell使用背景:公司的抓取管理平台,公司的产品人员每天都需要对各个地方的运营商网站看一些违规,投诉,公告等信息进行监控,为了保证产品人员不必要每天都上各个网站上看信息,所以针对内部需求,我们采用抓取策略,用程序模拟登录每一个网站,然后对我们要的信息,如公告信息进行抓取,当然抓取到网页之后还要配合HtmlParse技术进行处理,然后将我们提取出的信息显示到我们平台上,这样产品人员不需要登录每一个网站看信息,只要登录我们的抓取平台就可以看各个网站最新的公告等信息。因为每一个运营商的各个地方的网站是不一样的,也就是说抓取的各个业务是不一样的,针对这种情况,我们采用BeanShell来处理各个不同的业务。对不同的网站采用不同的BSH文件来处理,这样各个网站的抓取代码都放到不同的BSH文件中,不用每多抓取一个网站重新修改代码,重新发布。这样可以减少很多工作量。下面介绍一下BeanShell.
首先:BeanShell:是一种对象脚本语言(http://www.beanshell.org)。BeanShell的解释器是用Java写的,开源并且免费的,它的运作方式“它将脚本化对象看作简单闭包方法 (simple method closure)来支持,就如同在 JavaScript中的一样。 它具有以下的一些特点:使用Java反射API以提供Java语句和表达式 的实时解释执行;可以透明地访问任何Java对象和API;可以在命令行模式、控制台模式、小程序模式和远程线程服务器模式等四种模式下面运行;与在应用 程序中一样,可以在小程序中(Applet)正常运行(无需编译器或者类装载器);非常精简的解释器jar文件大小为175k ”。
使用BeanShell可以处理现实中不规则的业务,举个很典型的例子,我们知道,一个保险公司有很多险种,每个险种的投入和收益的算法是很复杂的,无法 用规则的关系数据库模型来描述,所以很多保险系统在处理险种的算法都是硬编码,如果要在系统中新加一个险种,是非常麻烦的,重新修改代码,重新发布,这对 开发/维护人员和客户都是很痛苦的,有了BeanShell,我们可以从这种痛苦中解脱出来,对每个险种使用不同的脚本,新加的险种我们新配一个脚本,这 样既可以满足业务多变的要求,又可以减少工作量,节约成本。
BeanShell的一个特点是动态执行Java代码,脚本文件改变不会影响当前脚本的调用,新脚本将在脚本的下一次调用生效,这不同于配置文件,配置文件改变一般都需要应用重启。
一个BeanShell的简单例子:
通过建立一个BeanShell解释器,使用eval()或source()命令,你可以在你的应用程序中求文本表达式的值和运行脚本。如果你希望在你的脚本内部使用一个对象,可以用set()方法传递对象的变量参照给BeanShell,并通过get()方法取得结果。
Java文件中
package com.an;
import java.io.FileNotFoundException;
import java.io.IOException;
import bsh.EvalError;
import bsh.Interpreter;
public class TestBeanShell {
/**
* 一个简单往BEANSHELL的文件传入参数,然后.java文件接收参数进行处理,然后返回结果
* 在.java文件中打印结果
*/
public static void main(String[] args) {
Interpreter inter = new Interpreter();
try {
//往bsh文件中传入参数(BeanShell文件中可以直接用,不用再定义)
inter.set("inValue",new Integer(1));
//调用并执行BSH文件
inter.source("src/com/an/testBeanShell.bsh");
//获得bsh文件中的结果
String resultValue = inter.get("outValue").toString();
System.out.println("=====输出结果========="+resultValue);
} catch (EvalError e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Bsh文件
//BeanShell中打印调用print()函数
print("=======传入的参数======="+inValue);
outValue = inValue+1;
一些常用的命令 (绿色的是我在项目中用到的) :
print():跟ava的System.out.println()非常的相像,除非它能保证输出总是命令行。print()也可以显示一些对象的类型(如数组),但比Java的更详细。
source(), run() : 将一个bsh脚本读到解释器或运行在另一个解释器。
frame() - 显示一个Frame或JFrame的GUI组件.
load(), save() - 载入和保存一个序列化的对象到一个文件.
cd(), cat(), dir(), pwd(), etc. - 类unix的shell命令。
exec() - 运行一个本地的程序。
javap() - 打印一个对象的方法和字段,类似于Java的javap命令。
setAccessibility() - 开启无限制的存取private 和protected的组件。
脚本方法
你可以在bsh中宣告和使用方法,就像在java的类中一样。只是bsh的方法可以有动态的(宽松的)参数和返回类型。
//bsh文件中的定义方法(两个数相加)
public int add(int num1 , int num2 ){
return num1+num2;
}
int result = add(1,2);
print(result);
注意:bsh文件的加载顺序是从上到下加载,上面例子中如果
int result = add(1,2);
print(result);
public int add(int num1 , int num2 ){
return num1+num2;
}
则会报
ourced file: src/com/an/testBeanShell.bsh : Typed variable declaration : Command not found: add( int, int ) : at Line: 1 : in file: src/com/an/testBeanShell.bsh : add ( 1 , 2 )
at bsh.Name.invokeLocalMethod(Unknown Source)
这个是BSH文件中常到的一个错误Unknown Source(未知源对象)。