`
abruzzi
  • 浏览: 444583 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

Java脚本技术应用实例

阅读更多

前言

一直以来都很喜欢可以自由扩展的软件,这一点应该已经在很多文章里提到,也重复过很多次了。但是,可扩展性,灵活性是开发人员最喜欢的东西了,本性难改。平时使用的开发环境如vim/emacs, IDE中的Eclipse/Netbeans, 浏览器FF/Chrome都具有强大而灵活的可扩展支持。而关于Java的脚本支持,我已经在数篇文章中提及,大多是关于JavaScript引擎rhino和宿主Java之间的合成,但是Java的脚步支持远不止这些,这篇文章尝试讨论一下,Java对其他语言的支持。

 

文中实现一个简单的工资计算器,本来是在来到新公司不久,用以和同事们交流脚本技术的应用时做的,后来又进行了一些改动,由于只是一个示例,界面很简单:


 

工资计算器

这个计算器很简单,从脚本中获取转换表(convertTable)及基数(base),即:当工资低于base时,直接返回工资数目,如果高于base,则根据转换表来查找税率,然后扣除税款,得到实际工资。由于在实际生活中,税率会不断的调整,这部分内容就应该放入脚本:

 

 

//base salary
var base = 2000;

/**
 * range and tax-rage convert table
 */
var convertTable = {
	"0~1000" : 0.1,
	"1000~2000" : 0.15,
	"2000~3000" : 0.2,
	"3000~5000" : 0.25,
	"5000~8000" : 0.3,
	"8000~-1" : 0.4
}

 

这里用-1表示不限。

 

在本文的示例中,每一个脚本会被作为一个"插件",插件可以被创建,激活,安装到应用程序中,安装之后的插件,存在于应用程序的运行时环境(RuntimeEnv),并可以在需要的时候被调用执行。比如在本文中,初始化应用程序的时候,init方法将被调用:

 

 

public void init(){
	Plugin system = new SimplePlugin("scripts/calc.js");
	system.activate();
	SimplePluginManager.getInstance().install(system);
}

 

然后,当按钮[计算实际工资 ]被点击的时候,会调用:

 

 

btnCalc.addActionListener(new ActionListener(){

	public void actionPerformed(ActionEvent e) {
		SimplePluginManager.getInstance().getPlugin("calc").activate();
		String salary = textSalary.getText();
		Double d = Double.parseDouble(salary);
		Double r = (Double)RuntimeEnv.getInstance().invokeFunction("calc", d);
		textReal.setText(String.valueOf(r));
	}
	
});

 

仔细观察会发现,在actionPerformed方法中,首先会将get出来的插件做激活(activate)动作,这是因为,如果应用程序在运行期间,脚本做过修改,则可以事实的反映在结果上。这里去掉了一些验证,比如根据脚本文件的lastModified 来判断是否需要激活等。

 


我将script做了一个简单的包装,成为插件,这个示例中的插件结构如上图所示。RuntimeEnv 为一个单例的实例,在应用中是始终有一个,每个组件都可以向这个实例请求执行脚本中的函数,至于函数的参数传递,类型转换等工作,由底层的脚本引擎来负责执行。

 

为了脚本可以被重复使用,可以将脚本先编译为“已编译脚本”对象:

 

 

/**
 * compile the script-file into an <code>CompiledScript</code> object
 * @return
 */
public CompiledScript compile(File file){
	Date scriptDate = new Date(file.lastModified());
	if(lastModified == null || scriptDate.after(lastModified)){
		Reader reader = null;
		try {
			reader = new FileReader(file);
			compiledScript = 
				RuntimeEnv.getInstance().getCompilableEngine().compile(reader);
			lastModified = scriptDate;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (ScriptException e) {
			e.printStackTrace();
		}finally{
			if(reader != null){
				try {
					reader.close();
				} catch (IOException e) {}	
			}
		}
	}
	
	this.status = Plugin.STATUS_LOADED;
	
	return compiledScript;
}
 

 

JavaScript版本的实现

下面为JavaScript版本的脚本,包含完整的计算逻辑,如果税率有新的调整,则仅仅需要修改脚本文件,甚至是在应用程序已处于运行状态。

 

 

//base salary
var base = 2000;

/**
 * range and tax-rage convert table
 */
var convertTable = {
	"0~1000" : 0.1,
	"1000~2000" : 0.15,
	"2000~3000" : 0.2,
	"3000~5000" : 0.25,
	"5000~8000" : 0.3,
	"8000~-1" : 0.4
}

/**
 * if the value is in the range?
 */
function inRange(value, range){
	var vs = range.split("~");
	var low, high;
	low = parseFloat(vs[0]);
	high = parseFloat(vs[1]);
	if(high == -1){//-1 means infinity
		high = Number.MAX_VALUE;
	}
	return (value >= low && value <= high);
}

/**
 * This is the function will be invoked by java program
 * @param salary
 * @return
 */
function calc(salary){
	var value;
	
	// less that or equals to base
	if(salary <= base){
		value = salary;
	}else{
		var f = salary - base;
		for(var item in convertTable){
			if(inRange(f, item)){
				value = salary - salary * convertTable[item];
				break;
			}
		}
	}
	
	return value;
}
 

 

Python版本的实现 

python版本仅仅作为JavaScript版本的翻译,但是在应用程序的角度来看,是没有任何差别的(可能在有错误的时候,会产生不同的异常)。

 

 

#
# author : juntao.qiu@gmail.com
#

# salary base
base = 2000

# convert table of range and rate
convertTable = {
	"0~1000" : 0.1,
	"1000~2000" : 0.15,
	"2000~3000" : 0.2,
	"3000~5000" : 0.25,
	"5000~8000" : 0.3,
	"8000~-1" : 0.4
}

# test value in range or not
def inRange(value, range):
	vs = range.split("~")
	low = float(vs[0])
	high = float(vs[1])
	
	if(high == -1):
		high = "inf"
	return (value >= low and value <= high)
	
# calculate salary without tax, invoked by java code
def calc(salary):
	value = None
	if(salary <= base):
		return salary
	else:
		f = salary - base
		for item in convertTable:
			if(inRange(f, item)):
				value = salary - salary * convertTable[item]
				break;
	return value

 

 

应该注意的是,jython的版本应该等于或者大于2.5.1,在2.5.1中,jython才实现了java的脚本扩展接口,我在测试的时候,jython的最新版本为2.5.1,不知道现在是否已经更新。

 

=========================================================

更新:

2011/1/23:添加了代码下载,感兴趣的朋友可以自行下载,需要注意的是,尽量使用JDK1.6版本,如果要验证jython,请使用2.5.1以上版本。JDK1.6中吧有JavaScript的实现

  • 大小: 18.5 KB
  • 大小: 181.3 KB
分享到:
评论
30 楼 kaneg 2011-01-31  
freish 写道
动态脚本执行的效率较低,尤其是js在java高并发的时候


其实并不是所有应用都需要考虑执行效率的。对于非大型多用户应用,开发的灵活性和及时性更重要。

举个例子,魔兽世界的插件使用lua语言编写的,任何人懂这个语言和游戏的人用一个文本编辑器就可以做出一个插件来,这样的便利性有多高可想而知
29 楼 chenyongxin 2011-01-25  
robiplus 写道
abruzzi 写道

就我个人的观点来说,几乎所有地方都可以加入脚本,当然,肯定会有效率,调试等方面的限制,因此我建议你在效率要求不高,而软件需求变化较频繁(考虑到实施等问题,最好使用脚本),或者软件的扩展性,定制性要求较高的场合。举例子的话,比如我上边提到的,编辑器,浏览器,IDE,图形编辑软件,播放器等等,都可以使用脚本。

总的来说,软件开发应该考虑到扩展性,尽量提供一个通用的基础平台,提供足够的接口,然后其他的功能都通过插件来实现,如Eclipse, Netbeans, Vim/Emacs等那样。

脚本语言的好处有很多:比如接口既定的情况下,无需编译宿主程序,即可直接运行;又比如脚本语言本身的灵活性,以及其他的一些特性,如JS的函数式编程;又比如,脚本语言本身无法完成的功能,如文件读写,网络访问,数据库访问等功能可以通过宿主语言来完成,等等。


仔细想了下。。。是的,用脚本来写程序中可plugin的地方真的不错,无需编译很给力。。。下次有机会尝试下,就不用去写配置文件+实现类来plugin了 哈哈

你可以尝试使用JavaCompiler,在将你编写的java源文件放入一个pugin目录下,使用的时候使用JavaCompiler进行编译,然后执行
28 楼 abruzzi 2011-01-25  
hu437 写道
abruzzi 写道
hu437 写道
楼主现在是2011年了,呵呵~~


C发明于1972年,知识分子们还在上山下乡,众多的javaeyer还没有出生,现在还不是有大量相关的书籍,文章发表?
Java发明于1991年,改革开放才刚刚真正的开始,众多的javaeyer还没见过计算机,现在还不是天天有相关的文章在出现?
2011年怎么了?呵呵。


呵呵 LZ理解错了,我没有其他意思,只是看到你下面写的更新是2010/1/23 只是说现在是2011年了,不是2010


哦,不好意思,我以为你是再说技术过时的问题,我修改下更新时间(没有上下文,很容易误解,不好意思)。
27 楼 tooor 2011-01-25  
www.create-world.com

编写服务器端脚本,开发多人在线实时交互应用

性能完全没有问题
26 楼 hu437 2011-01-24  
abruzzi 写道
hu437 写道
楼主现在是2011年了,呵呵~~


C发明于1972年,知识分子们还在上山下乡,众多的javaeyer还没有出生,现在还不是有大量相关的书籍,文章发表?
Java发明于1991年,改革开放才刚刚真正的开始,众多的javaeyer还没见过计算机,现在还不是天天有相关的文章在出现?
2011年怎么了?呵呵。


呵呵 LZ理解错了,我没有其他意思,只是看到你下面写的更新是2010/1/23 只是说现在是2011年了,不是2010
25 楼 abruzzi 2011-01-24  
生活小丑 写道
嗯,好想法!弱弱的问一句,楼主这图是什么工具画出来的?


Netbeans 6.5的UML插件。很奇怪的是,6.9版本竟然把UML插件去掉了,换成一个叫VP的东东。
24 楼 生活小丑 2011-01-24  
嗯,好想法!弱弱的问一句,楼主这图是什么工具画出来的?
23 楼 robiplus 2011-01-24  
abruzzi 写道

就我个人的观点来说,几乎所有地方都可以加入脚本,当然,肯定会有效率,调试等方面的限制,因此我建议你在效率要求不高,而软件需求变化较频繁(考虑到实施等问题,最好使用脚本),或者软件的扩展性,定制性要求较高的场合。举例子的话,比如我上边提到的,编辑器,浏览器,IDE,图形编辑软件,播放器等等,都可以使用脚本。

总的来说,软件开发应该考虑到扩展性,尽量提供一个通用的基础平台,提供足够的接口,然后其他的功能都通过插件来实现,如Eclipse, Netbeans, Vim/Emacs等那样。

脚本语言的好处有很多:比如接口既定的情况下,无需编译宿主程序,即可直接运行;又比如脚本语言本身的灵活性,以及其他的一些特性,如JS的函数式编程;又比如,脚本语言本身无法完成的功能,如文件读写,网络访问,数据库访问等功能可以通过宿主语言来完成,等等。


仔细想了下。。。是的,用脚本来写程序中可plugin的地方真的不错,无需编译很给力。。。下次有机会尝试下,就不用去写配置文件+实现类来plugin了 哈哈
22 楼 DOCDOC 2011-01-24  
eclipse-my 写道
动态脚本确实效率比较低,顾此失彼

我觉得这是一个取舍的问题,并没有完美的方案
21 楼 abruzzi 2011-01-24  
robiplus 写道
大哥可以举个列子说下什么时候在Java中掺脚本比较适用呢? 

打个比方哈。。。小弟初学很不解  而且最终脚本也是转为java字节码,除了获得灵活简洁的语法还有其他原因不


就我个人的观点来说,几乎所有地方都可以加入脚本,当然,肯定会有效率,调试等方面的限制,因此我建议你在效率要求不高,而软件需求变化较频繁(考虑到实施等问题,最好使用脚本),或者软件的扩展性,定制性要求较高的场合。举例子的话,比如我上边提到的,编辑器,浏览器,IDE,图形编辑软件,播放器等等,都可以使用脚本。

总的来说,软件开发应该考虑到扩展性,尽量提供一个通用的基础平台,提供足够的接口,然后其他的功能都通过插件来实现,如Eclipse, Netbeans, Vim/Emacs等那样。

脚本语言的好处有很多:比如接口既定的情况下,无需编译宿主程序,即可直接运行;又比如脚本语言本身的灵活性,以及其他的一些特性,如JS的函数式编程;又比如,脚本语言本身无法完成的功能,如文件读写,网络访问,数据库访问等功能可以通过宿主语言来完成,等等。
20 楼 robiplus 2011-01-24  
大哥可以举个列子说下什么时候在Java中掺脚本比较适用呢? 

打个比方哈。。。小弟初学很不解  而且最终脚本也是转为java字节码,除了获得灵活简洁的语法还有其他原因不
19 楼 eclipse-my 2011-01-24  
动态脚本确实效率比较低,顾此失彼
18 楼 DOCDOC 2011-01-24  
abruzzi 写道
glovebx 写道
复杂脚本能支持吗?


你是说例子中的计算器是否支持复杂脚本吗?由于内部使用的rhino引擎,并没有进行任何方面的限制,因此理论上,可以支持任意复杂的脚本。比如自己用JavaScript来实现OO语言系统,利用宿主语言Java的多线程,UI等便利来改善JS应用的性能和外观等。

复杂脚本毫无问题,前年一个项目,业务规则就是用JS写的,跑Rhino
17 楼 abruzzi 2011-01-24  
freish 写道
freish 写道
abruzzi 写道
freish 写道
动态脚本执行的效率较低,尤其是js在java高并发的时候


如果是纯解释的话,效率的确很低,但是Java脚本框架中提供CompiledScript接口,如果脚本引擎实现该接口,则可以将脚本预先编译,效率就基本没有问题了。这有点像JDBC中的PreparedStatement,效率就没什么问题了。


还是不够高

虽然可以编译,但它的执行比java慢的不是一个数量级

在很多工作流产品中, 都拿rhino来做迁移的判断以及前后置脚本的执行,在高并发情况下,java执行只要几十或者几百毫秒的时候,js要2秒左右


并且,编译会带来一个问题

有许多js脚本是通过java代码生成的,这就意味着生成的js的长度在某些情况下会很长,当超过64k的时候,编译会失败的

http://hi.baidu.com/freish/blog/item/5d02e450f4dbf24e1138c2ed.html


确实会有此类的问题,这个在之前的项目中遇到过:一个开发人员将JSP文件写的非常大,业务逻辑(很多的条件判断等代码)全部都写在JSP中,最后生成Servlet的时候,超出了方法的长度限制。不过我认为这种情况可以通过设计来避免,比如类/方法的拆分等。也就是说,这并非引擎有什么问题,比如,你手工的将一个Java方法写的超过64k,同样会有这样的问题。
16 楼 abruzzi 2011-01-24  
freish 写道
abruzzi 写道
freish 写道
动态脚本执行的效率较低,尤其是js在java高并发的时候


如果是纯解释的话,效率的确很低,但是Java脚本框架中提供CompiledScript接口,如果脚本引擎实现该接口,则可以将脚本预先编译,效率就基本没有问题了。这有点像JDBC中的PreparedStatement,效率就没什么问题了。


还是不够高

虽然可以编译,但它的执行比java慢的不是一个数量级

在很多工作流产品中, 都拿rhino来做迁移的判断以及前后置脚本的执行,在高并发情况下,java执行只要几十或者几百毫秒的时候,js要2秒左右



遇到高并发这种情况,我没有做过实验,可能确实效率较低。毕竟,可能会有诸如校验,类型转换之类的动作,这个我下来再看看。就一般应用(并非服务器如httpd之类)而言,如编辑器,浏览器,IDE等,脚本作为插件的形式存在于应用程序的生命周期中,不会涉及到高并发这类的需求。如果是freish兄所说的这种情况,通常会把代码编译成动态库(C)或者可供虚拟机直接执行的bytecode(Java), 脚本语言本身则无法做到。
15 楼 li2005 2011-01-24  
第一次看到插件方面的文章,谢谢分享
14 楼 freish 2011-01-24  
freish 写道
abruzzi 写道
freish 写道
动态脚本执行的效率较低,尤其是js在java高并发的时候


如果是纯解释的话,效率的确很低,但是Java脚本框架中提供CompiledScript接口,如果脚本引擎实现该接口,则可以将脚本预先编译,效率就基本没有问题了。这有点像JDBC中的PreparedStatement,效率就没什么问题了。


还是不够高

虽然可以编译,但它的执行比java慢的不是一个数量级

在很多工作流产品中, 都拿rhino来做迁移的判断以及前后置脚本的执行,在高并发情况下,java执行只要几十或者几百毫秒的时候,js要2秒左右


并且,编译会带来一个问题

有许多js脚本是通过java代码生成的,这就意味着生成的js的长度在某些情况下会很长,当超过64k的时候,编译会失败的

http://hi.baidu.com/freish/blog/item/5d02e450f4dbf24e1138c2ed.html
13 楼 freish 2011-01-24  
abruzzi 写道
freish 写道
动态脚本执行的效率较低,尤其是js在java高并发的时候


如果是纯解释的话,效率的确很低,但是Java脚本框架中提供CompiledScript接口,如果脚本引擎实现该接口,则可以将脚本预先编译,效率就基本没有问题了。这有点像JDBC中的PreparedStatement,效率就没什么问题了。


还是不够高

虽然可以编译,但它的执行比java慢的不是一个数量级

在很多工作流产品中, 都拿rhino来做迁移的判断以及前后置脚本的执行,在高并发情况下,java执行只要几十或者几百毫秒的时候,js要2秒左右
12 楼 abruzzi 2011-01-24  
freish 写道
动态脚本执行的效率较低,尤其是js在java高并发的时候


如果是纯解释的话,效率的确很低,但是Java脚本框架中提供CompiledScript接口,如果脚本引擎实现该接口,则可以将脚本预先编译,效率就基本没有问题了。这有点像JDBC中的PreparedStatement,效率就没什么问题了。
11 楼 abruzzi 2011-01-24  
hu437 写道
楼主现在是2011年了,呵呵~~


C发明于1972年,知识分子们还在上山下乡,众多的javaeyer还没有出生,现在还不是有大量相关的书籍,文章发表?
Java发明于1991年,改革开放才刚刚真正的开始,众多的javaeyer还没见过计算机,现在还不是天天有相关的文章在出现?
2011年怎么了?呵呵。

相关推荐

    java源码包---java 源码 大量 实例

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    JAVA上百实例源码以及开源项目源代码

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    Java Web应用开发 12 课堂案例-使用脚本程序.docx

    Java Web应用开发 12 课堂案例-使用脚本程序.docx 学习资料 复习资料 教学资源

    JAVA上百实例源码以及开源项目

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part2

    本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。.  本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...

    Java EE Web开发实例精解完整光盘

    JAVA EE Web应用系统从逻辑上可划分为表现层、业务层和持久层,为了使读者对JAVA EE编程技术获得全面系统的了解,《Java EE Web开发实例精解》以JAVA EE Web应用系统的逻辑加构为主线,通过多个典型工程实例对上述三...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part3

    本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。.  本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...

    Web应用开发技术:jsp1-2

    本书通过丰富的实例,系统地讲解了Java Server Pages(JSP)动态Web应用开发技术。主要内容包括:Web基础知识,主流动态Web技术介绍,JSP开发运行环境的搭建,MyEclipse智能助手的使用,HTML语言, JSP脚本语言——...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part4

    本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。.  本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...

    Ajax安全技术

    通读《AJAX安全技术》你将看到很多用于阐述关键知识点的真实Ajax安全漏洞案例。在书中还讲到保护Ajax应用的特殊方法,包括每种主要Web编程语言(.NET、Java和PHP)及流行新语言Ruby on Rails。, 《AJAX安全技术》一...

    java 学生成绩管理系统(源码+数据库脚本+文档).rar

    【实例简介】我们在学习了JavaWeb各种基础知识之后,最需要做的就是学会应用,开发一些项目把所学的知识应用起来,下面的这个《JavaWeb学生成绩管理系统》实战项目就是比较好的练手教程。 开发环境:Java、MySQL ...

    java源码包4

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    java源码包3

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part5

    本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。.  本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...

    java源码包2

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    java源码包JSP实例源码JAVA开发源码65个合集.zip

    java源码包JSP实例源码JAVA开发源码65个合集: Java自定义光标程序源码.rar Jav动画图标源码(显示GIF图像).rar JLoading Java版的Mp3下载工具.rar JSP 动态数据菜单.rar JSP 学生管理系统(全部代码+数据库).rar ...

    基于SSM整合的Java案例设计源码

    本设计源码提供了一个基于SSM整合的Java案例。项目包含173个文件,主要使用Java、HTML、CSS和JavaScript编程语言。...该案例适合用于学习和实践Java、HTML、CSS和JavaScript技术,以及开发基于SSM整合的应用程序。

    一个使用Java和SQL语言设计的超市管理系统完全可以用于期末作业实现

    对于缺乏实践经验的学生,本课程还提供了丰富的实例演示和案例分析,帮助他们快速掌握基本的开发技能并获得实践经验。总之,本课程是一个完整的Java数据库应用课程,旨在帮助学生掌握数据库开发的核心技能,拥有从...

Global site tag (gtag.js) - Google Analytics