如果退出Python解释程序然后再进入,原有的定义(函数和变量)就丢失了。所以,如
果需要写长一点的程序,最好用一个文本编辑程序为解释程序准备输入,然后以程序文
件作为输入来运行Python解释程序,这称为准备脚本(script)。当你的程序变长时,
最好把它拆分成几个文件以利于维护。你还可能想在几个程序中都使用某个很方便的函
数,但又不想把函数定义赋值到每一个程序中。
为了支持这些,Python有一种办法可以把定义放在一个文件中然后就可以在一个脚本中
或交互运行中调用。这样的文件叫做一个模块;模块中的定义可以导入其它模块或主模
块(主模块指在解释程序顶级执行的脚本或交互执行的程序所能访问的变量集合)。
模块是包含了Python定义和语句的文件。文件名由模块名加上后缀“.py”构成。在模块
内,模块的名字(作为一个字符串)可以由全局变量__name__的值获知。例如,在Pyth
on的搜索路径中用你习惯使用的文本编辑器(Python 1.5.2包含了一个用Tkinter编写的
IDLE集成开发环境,MS Windows下有一个PythonWin界面也可以进行Python程序编辑)生
成一个名为“fibo.py ”的文件,包含如下内容:
# Fibonacci numbers module
def fib(n):# 输出小于n的Fibonacci序列
a, b = 0, 1
while b <n:
print b,
a, b = b, a+b
def fib2(n): # 返回小于n的Fibonacci序列
result = []
a, b = 0, 1
while b <n:
result.append(b)
a, b = b, a+b
return result
然后进入Python解释程序(在IDLE或PythonWin中可以直接进入解释程序窗口),用如下
命令可以导入模块:
>>>import fibo
这不会把模块fibo中的函数的名字直接引入当前的符号表,这只是把模块名fibo引入。
可以用模块名来访问其中的函数:
>>>fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>>fibo.fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>>fibo.__name__
'fibo'
如果经常使用某个函数可以给它赋一个局部名字:
>>>fib = fibo.fib
>>>fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
6.1 模块的进一步介绍
模块除了可以包含函数定义之外也可以包含可执行语句。这些可执行语句用来初始化模
块,它们只在模块第一次被导入时执行。
每个模块有自己私有的符号表,这个私有符号表对于模块中的所有函数而言却是它们的
全局符号表。因此,模块作者可以在模块中使用全局变量而不需担心与模块用户的全局
变量冲突。另一方面,如果你有把握的话也可以用访问模块中函数的格式,即modname.
itemname的方法来修改模块中的全局变量。
模块可以导入其它模块。我们通常把所有的导入语句放在模块(或脚本)的开始位置,
这不是规定要求的。导入的模块名放入模块的全局符号表中。
导入还有另一种用法,可以把模块中的名字直接导入使用者的符号表。例如:
>>>from fibo import fib, fib2
>>>fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这不会把模块名导入使用者的符号表中(例如,上面例子中fibo就没有定义)。
还有一种办法可以导入一个模块中定义的所有名字:
>>>from fibo import *
>>>fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这可以把模块中除了以下划线结尾的所有名字导入。
6.1.1 模块搜索路径
在导入名为spam的模块时,解释程序先在当前目录中寻找名为“spam.py”的文件,然后
从环境变量PYTHONPATH所定义的目录列表中寻找。PYTHONPATH的用法和可执行文件的搜
索路径PATH用法相同,都是一个目录列表。当PYTHONPATH未设置的时候,或者文件仍找
不到,则搜索继续在安装时设定的缺省路径搜索,在Unix中,这通常是“.:/usr/local
/lib/python” 。
实际上,模块是按变量sys.path指定的路径搜索的,此变量在解释程序启动时初始化为
包含输入脚本的目录(或当前路径),PYTHONPATH和安装缺省路径。这样,用户可以通
过修改sys.path 来修改和替换模块搜索路径。参见后面关于标准模块的一节。
6.1.2 “编译”的Python文件
为了提高调用许多标准模块的小程序的启动时间,一个重要的措施是,如果在找到“sp
am.py ”的目录中存在一个名为“spam.pyc”的文件,就认为此文件包含了模块spam的
一个所谓“ 字节编译”版本。用于生成“spam.pyc”的“spam.py”的修改时间被记入
了“spam.pyc”中,如果记录的修改时间与现在文件的时间不相符的话就忽略编译文件
。
一般不需要自己生成“spam.pyc”这样的编译文件。每当“spam.py”成功编译后解释程
序就尝试写编译版本“spam.pyc”,如果不可写也不会出错;如果因为某种原因此文件
没有写完则生成的“spam.pyc”被识别为不完整的而被忽略。编译文件“spam.pyc”的
格式是不依赖于平台的,所以不同结构的机器可以共享Python模块目录。
下面是对专家的一些窍门:
如果Python解释程序是以-O标志启动的,将生成优化的编译代码,保存在“.pyo”文件
中。目前优化不是很多,现在只是去掉assert语句和SET_LINENO指令。使用了-O标志时
,所有字节码都是优化的,“.pyc”文件被忽略,“.py”文件被编译为优化的字节码。
给Python解释程序两个优化标志(-OO)产生的优化代码有时会导致程序运行不正常。目
前双重优化只从字节码中删除了__doc__字符串,使得“.pyo”文件较小。有些程序可能
是依赖于文档字符串的,所以只有在确知不会有问题时才可以使用这样的优化。
从“.pyc”或“.pyo”读入的程序并不能比从“.py”读入的运行更快,它们只是调入速
度更快一些。
如果一个程序是用在命令行指定脚本文件名的方式运行的,脚本的字节码不会写入“.p
yc ”或“.pyo”文件。所以如果把程序的主要代码都移入一个模块,脚本中只剩下导入
该模块的引导程序则可以略微缩短脚本的启动时间。
可以有叫做“spam.pyc”(当用了-O标志时为“spam.pyo”)的文件而没有对应的源文
件“spam.py”。这可以用来分发一个比较难反编译的Python代码库。
模块compileall可以把一个目录中所有模块编译为“.pyc”文件(指定了-O选项时编译
为“.pyo”文件)。
6.2 标准模块
Python带有一个标准模块库,在另一个文档《Python库参考》中进行了描述。一些模块
直接编入了解释程序中,这些模块不是语言的核心,为了运行效率或者为了提供对于系
统调用这样的系统底层功能而编入了解释程序中。提供那些模块是编译时的选择,例如
,amoeba模块只在提供amoeba底层指令的系统中才能提供。
有一个模块值得特别重视:sys模块,每一个Python解释程序中都编译入了这个模块。变
量sys.ps1和sys.ps2定义了交互运行时的初始提示和续行提示。
>>>import sys
>>>sys.ps1
'>>>'
>>>sys.ps2
'... '
>>>sys.ps1 = 'C>'
C>print 'Yuck!'
Yuck!
C>
这两个变量只在解释程序以交互方式运行时才有定义。
变量sys.path是一个字符串列表,由它确定解释程序的模块搜索路径。它被初始化为环
境变量PYTHONPATH所指定的缺省路径,环境变量没有定义时初始化为安装时的缺省路径
。可以用标准的列表操作修改这个搜索路径,例如:
>>>import sys
>>>sys.path.append('/ufs/guido/lib/python')
6.3 dir()函数
内置函数dir()用于列出一个模块所定义的名字,它返回一个字符串列表:
>>>import fibo, sys
>>>dir(fibo)
['__name__', 'fib', 'fib2']
>>>dir(sys)
['__name__', 'argv', 'builtin_module_names', 'copyright', 'exit',
'maxint', 'modules', 'path', 'ps1', 'ps2', 'setprofile', 'settrace',
'stderr', 'stdin', 'stdout', 'version']
没有自变量时,dir()列出当前定义的名字。
>>>a = [1, 2, 3, 4, 5]
>>>import fibo, sys
>>>fib = fibo.fib
>>>dir()
['__name__', 'a', 'fib', 'fibo', 'sys']
注意dir()列出了所有各类名字:变量名、模块名、函数名,等等。dir()不会列出内置
函数、变量的名字。要想列出内置名字的话需要使用标准模块__builtin__:
>>>import __builtin__
>>>dir(__builtin__)
['AccessError', 'AttributeError', 'ConflictError', 'EOFError', 'IOError',
'ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
'MemoryError', 'NameError', 'None', 'OverflowError', 'RuntimeError',
'SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError',
'ZeroDivisionError', '__name__', 'abs', 'apply', 'chr', 'cmp', 'coerce',
'compile', 'dir', 'divmod', 'eval', 'execfile', 'filter', 'float',
'getattr', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'len', 'long',
'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'range', 'raw_input',
'reduce', 'reload', 'repr', 'round', 'setattr', 'str', 'type', 'xrange']
6.4 包
Python中可以用“包”来组织Python的模块名字空间,名字引用时可以用“带点的模块
名。例如,模块名A.B代表包“A”内名为“B”的子模块。正如使用模块可以使不同模块
的作者不用顾虑彼此的全局变量名会冲突,使用带点的模块名可以使多模块包如NumPy和
PIL的作者不需要担心彼此的模块名会冲突。
假设你有一系列处理声音文件和声音数据的模块(称为一个“包”)。有许多种不同的
声音文件格式(通常用扩展名来识别,如“wav”,“.aiff”,“.au”),所以你可能
需要制作并维护一组不断增加的模块来处理不同文件格式的转换。你还可能需要对声音
数据进行许多不同的操作(如混音、回响、均衡、产生模拟立体声效果),所以你还需
要不断增加模块来执行这些操作。一下是你的程序包的可能的结构(用一个分层文件系
统表示):
Sound/ 顶层包
__init__.py 初始化音响包
Formats/ 用于文件格式转换的子程序包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
Effects/ 用于音响效果的子程序包
__init__.py
echo.py
surround.py
reverse.py
...
Filters/ 用于滤波的子程序包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
包目录中的“__init__.py”文件是必须得,用来指示Python把这个目录看成包,这可以
防止有相同名字如“string”的子目录掩盖住在搜索路径后面一些出现的模块定义。在
最简单的情况下,“__init__.py”可以是一个空文件,它也可以包含初始化包所需的代
码,和设置“__all__”变量,这些后面会加以讨论。
包的用户可以从包中导入单独的模块,如:
import Sound.Effects.echo
这可以把子模块Sound.Effects.echo导入。要引用它也必须用全名,例如:
Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4)
导入子模块的另一种办法是:
from Sound.Effects import echo
这同样也导入子模块echo,但调用时不需写包前缀,所以可以用如:
echo.echofilter(input, output, delay=0.7, atten=4)
另外一种写法是直接导入所需的函数或变量:
from Sound.Effects.echo import echofilter
这一次同样是调入了子模块echo,但是使其函数echofilter直接可用:
echofilter(input, output, delay=0.7, atten=4)
注意使用“from 包 import 项”这样的格式时,导入的项可以是包的一个子模块(或子
包),也可以是包内定义的其它名字如函数、类、变量。导入语句首先查找包内是否定
义了所需的项,如果没有则假设它是一个模块然后调入。如果找不到,结果引起Import
Error。
相反的,当使用“import item.subitem.subsubitem”这样的格式时,除最后一个外其
它各项都应该是包,最后一项可以是包也可以是模块,不允许是前面一项内部定义的类
、函数或变量。
6.4.1 从包中导入*
现在,如果用户写“from Sound.Effects import *”会发生什么情况?理想情况下我们
希望这应该扫描文件系统,找到所有包内的子模块并把它们都导入进来。不幸的是这种
操作在Mac和Windows平台上不能准确实现,这两种操作系统对文件名的大小写没有准确
信息。在这些平台上,不知道名为“ECHO.PY”的文件会作为模块echo、Echo还是ECHO被
导入。(例如,Windows 95在显示文件名时总是讨厌地把第一个字母大写)。DOS的8+
3文件名限制更是对长模块名造成了有趣的困难。
这个问题的唯一解决办法是由模块作者显式地提供包的索引。引入*的import语句遵循如
下规定:如果包的“__init__.py”文件定义了一个名为“__all__”的列表,这个列表
就作为从包内导入*时要导入的所有模块的名字表。因此当包的新版本发布时需要包的作
者确保这个列表是最新的。包的作者如果认为不需要导入*的话也可以不支持这种用法。
例如,文件Sounds/Effects/__init__.py 可以包含如下代码:
__all__ = ["echo", "surround", "reverse"]
这意味着from Sound.Effects import *将从Sound包中导入指定的三个子包。
如果没有定义__all__,则from Sound.Effects import *语句不会导入Sound.Effects包
中的所有子模块;此语句只能保证Sound.Effects被导入(可能是执行其初始化代码“_
_init__.py ”)并导入包中直接定义的名字。这包括由“__init__.py”定义的任何名
字和显式导入的子模块名。这也包括模块中已经在前面用import显式地导入的子模块,
例如:
import Sound.Effects.echo
import Sound.Effects.surround
from Sound.Effects import *
在这个例子中,echo和surround模块被导入当前名字空间,因为它们在执行from...imp
ort 语句时已定义(在定义了__all__的情况下这一点也是成立的)。
注意用户应尽量避免使用从模块或包中导入*的做法,因为这样经常导致可读性差的代码
。尽管如此,在交互运行时可以用导入*的办法节省敲键次数,而且有些模块在设计时就
考虑到了这个问题,它们只输出遵循某种约定的名字。注意,from 包 import 特定子模
块的用法并没有错,实际上这还是我们推荐的用法,除非程序还需要用到来自其它包的
同名的子模块。
6.4.2 包内部引用
子模块常常需要彼此引用。例如,模块surround可能要用到模块echo。事实上,这样的
引用十分常见,所以import语句首先从子模块的所在包中寻找要导入的子模块才在标准
模块搜索路径查找。所以,模块surround只要写import echo或from echo import echo
filter。如果在包含本模块的包中没有找到要导入的模块,import语句将去寻找指定名
字的顶级模块。
当包组织成子包时(比如例中的Sound包),没有一种简单的办法可以引用兄弟包中的子
模块――必须使用子模块的全名。例如,如果模块Sound.Filters.vocoder要引用Sound
.Effects 包中的echo模块,它可以用Sound.Effects import echo。
通过执行类代码体创建或扩展类Class的对象
ruby 代码
class [scope::] classname [
body
end
注意:
1、如果superexpr存在,那么该superexpr应当是一个以Class对象为结果的表达式,而且它将是被定义的类的超类。
如果省略superexpr,则默认为类Object
2、所有类定义是在代码执行时候生成的。
3、所有类定义在执行后都是一个Class类的对象。
4、所有的这些对象都将赋给名为classname的常量。
5、使用域作用符(::)可以为类定义中的classname前置一个已存在的类或模块名。这样会将新的定义插入到前面定义
的模块和/或类的名字空间中,但不是在这些外部类的作用域中解释此定义。前面带有域作用符的classname将被放置在
那个类或模块所处的顶层(这样作有个什么好处)
方式二、匿名类
ruby 代码
class <<obj
body
end
该类是一个匿名类,将会和指定的对象关联。这时这个匿名类将是该关联对象的虚拟类,原来该对象的类,变成了该虚拟 类的超类。
注意:如果直接在类定义外,给你对象新增一些方法和这样为一个对象创建一个类是等价的。同样是创建一个虚拟类。
Ruby类定义和其他语言不一样,其他语言都是在编译期处理的。而ruby是在代码运行时候根据类的定义创建的。这样如下特点:
1、可以在运行时候决定类的定义。这样根据不同条件,得出不同的类的定义结构
2、如果类定义是可执行的代码,那么它一定是在某个对象的上下文中执行的。Ruby中直接把该类作为当前对象
3、这个对象的类属于Class。说明,所有创建类就是一个对象,他们的类就是Class
太多了!对不起!没有给您找完
BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法BeanShell是一种松散类型的脚本语言(这点和JS类似)
BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,
具有对象脚本语言特性,非常精简的解释器jar文件大小为175k。
BeanShell执行标准Java语句和表达式,另外包括一些脚本命令和语法。
官网:http://www.BeanShell.org/
BeanShell 将成为Java平台上的第三种编程语言,前两种分别为java和Groovy(也是一种脚本语言)
下载与安装:
jar: http://www.beanshell.org/download.html
src: http://www.beanshell.org/developer.html
或使用SVN:
svn co http://ikayzo.org/svn/beanshell
doc: http://www.beanshell.org/docs.html
设置环境:
(1) 把bsh-xx.jar放到$JAVA_HOME/jre/lib/ext文件夹下(不推荐)
(2) UNIX: export CLASSPATH=$CLASSPATH:bsh-xx.jar(推荐)
(3) WINDOWS: set classpath %classpath%bsh-xx.jar(推荐)
运行方式:
(1) 界面UI方式 : java bsh.Console
(2) 命令行方式 : java bsh.Interpreter
(3) 运行脚本文件: java bsh.Interpreter filename [args]
简单举例(windows):
设置好环境变量classpath, 打开dos或CMD窗口, 键入命令:
java bsh.Console
出现BeanShell的"Bsh Workspace"界面,表示设置成功,BeanShell开始运行
在使用上面命令出现的控制台界面中进行下面的测试:
bsh % foo="Foo" //定义变量foo
bsh % four=(2+2)*2/2 //定义变量four并初始化为(2+2)*2/2
bsh % print(foo+"="+four) //输出 Foo=4
Foo=4 //输出结果
bsh % for(i=0i<5i++)print(i)//循环打印0~4
0
1
2
3
4
bsh % button = new JButton("MyButton")//实例化一个Button对象
bsh % frame = new JFrame("MyFrame")//实例化一个Frame对象
bsh % frame.getContentPane().add(button,"Center")//将Button对象加入Frame对象的面板中
bsh % frame.pack()//打包
bsh % frame.setVisible(true)//设置Frame对象的可见性回车后会弹出一个GUI界面
bsh %
完整代码:
foo = "Foo"
four = (2 + 2)*2/2
print( foo + " = " + four )
for (i=0i<5i++)
print(i)
button = new JButton( "My Button" )
frame = new JFrame( "My Frame" )
frame.getContentPane().add( button, "Center" )
frame.pack()
frame.setVisible(true)
在窗口中输入上面的代码, 敲回车执行,弹出一个有Button的GUI界面.
说明:
因为BeanShell是松散类型的脚本语言因此可以直接写:
foo = "Foo"
four = (2 + 2)*2/2
也就是foo与four均不需要变量类型的声明,给赋什么类型的值,就是什么类型的变量
print()是BeanShell提供一种简单的打印命令,相当于java中的System.out.println()
脚本中所写的方法范例:
int addTwoNumbers( int a, int b ) {
return a + b
}
sum = addTwoNumbers( 5, 7 )// 12
也可以使用动态的变量类型(无状态)方法
add(a, b){
return a + b
}
foo = add(1, 2)// 3
foo = add(1, "2")//"12"
特别注意:
只要有一个为字符串全部按照字符串处理,
系统不会根据1是数字在前把"2"转换成数字处理
foo = add("Oh", " baby")// "Oh baby"
脚本中实现接口:
实现任何接口需要JDK1.3或者更高,可以使用缺省的java匿名类的语法实现一个接口类,例如:
ActionListener scriptedListener = new ActionListener() {
actionPerformed( event ) { ... }
}
不需要实现接口的所有的方法,只需要实现你使用的方法即可, 如果使用你没有实现的方法,
BeanShell将抛出一个错误,如:
ml = new MouseListener() {
mousePressed( event ) { print("test")}
// handle the rest
invoke( name, args ) {
print("Method: "+name+" invoked!")
}
}
脚本中定义对象:
例一:
使用特殊的关键字this可以创建一个对象(根JS类似)
foo() {
print("foo")
x=5
bar() {
print("bar")
}
return this
}
myfoo = foo()// prints "foo"
print( myfoo.x )// prints "5"
myfoo.bar()// prints "bar"
例二:
在java标准语言中可以使用this返回一个类的一个实例
// MyClass.java
MyClass {
Object getObject() {
return this// return a reference to our object
}
}
在这个例子中getObject() 方法是返回MyClass类的一个实例
在BeanShell中对象中的变量只是局部的变量在对象内可以使用,
在对象外是不可以使用的(不同于前面for-loop,if-else中的使用)
// Define the foo() method:
foo() {
bar = 42
print( bar )
}
// Invoke the foo() method:
foo()// prints 42
print(bar)// Error, bar is undefined here
这里的print(bar)会抛出异常:
// Error: EvalError: Undefined argument: bar : at Line: 1 : in file: <unknown file>: ( bar )
但可以使用this返回对象,使用对象加上"."运算符引用属性(类似JS)
foo() {
bar = 42
return this
}
fooObj = foo()
print( fooObj.bar )// prints 42
同样对象中也可以定义一些方法,例如:
foo() {
int a = 42
bar() {
print("The bar is open!")
}
//bar()
return this
}
// Construct the foo object
fooObj = foo()// prints "the bar is open!"
// Print a variable of the foo object
print ( fooObj.a ) // 42
// Invoke a method on the foo object
fooObj.bar()// prints "the bar is open!"
也可以定义bar()和foo()也带参数:
foo() {
bar(int a) {
print("The bar is open!" + a)
}
return this
}
foo().bar(1)
也可以把bar()方法定义到对象外面
foo() {
return this
}
bar(int a) {
print("The bar is open!" + a)
}
foo().bar(1)//其实就是bar(1)
BeanShell一种松散的脚本语言, 有很多中声明的方法可以使用:
This super global
This 是引用当前对象
Super 是引用父亲对象
Global 是引用最上层对象
super.super.super...foo = 42// Chain super. to reach the top
global.foo = 42
BeanShell(Bsh)调用外部Java类的两种方式:
(1) 方法一(直接调用.java源文件)
sourceRelative("xxx.java")
利用sourceRelative()方法把java的源代码引入bsh环境中,然后进行调用。
这种方法不需要编译引用的java源文件,缺点也很明显"无法利用javac来检查java代码"。
(2) 方法二(通过引用.class二进制文件)
import xxx
利用传统的java import机制来获取java类,然后在bsh环境中进行调用。
需要编译.java文件
方便调用第三方的Java API(因为通常情况下无法得到API的源码,即使得到了文件会远大于jar包的大小)。
BeanShell介绍:
我们知道,现在有很多脚本语言,大家平时使用的比较多的包括Perl,Python,Ruby,JavaScript,Groovy,
在这里我要介绍的是另外一个对象脚本语言BeanShell(http://www.BeanShell.org)。
BeanShell的解释器是用Java写的,开源并且免费的,
引用open-open上面的话来说明它的运作方式:
它将脚本化对象看作简单闭包方法(simple method closure)来支持,
就如同在Perl和JavaScript中的一样。它具有以下的一些特点:
* 使用Java反射API以提供Java语句和表达式的实时解释执行
* 可以透明地访问任何Java对象和API
* 可以在命令行模式、控制台模式、小程序模式和远程线程服务器模式等四种模式下面运行
* 与在应用程序中一样,可以在小程序中(Applet)正常运行(无需编译器或者类装载器)
* 非常精简的解释器, jar文件中的Intepreter.class大小为150+k"。
每一种脚本语言都有它的使用场景,而正是因为其在某些场景的使用而使语言本身得到了发扬光大,
比如Ruby正是因为Ruby On Rails这个Web框架的流行而得到开发者的关注,Groovy也一样
BeanShell可不能再有Web框架,Java世界的Web框架已经太多了,够让人头痛的了
BeanShell是Java语法,所以对Java开发者来说,很快就可以上手,
BeanShell不能像Ruby,Perl,Ruby一样可以占据一个系统的主体,
而只能在一些小的地方发挥"螺丝钉"的作用。
笔者与BeanShell结缘是通过一个开源工作流引擎-OBE(这个鸟东西现在好像没什么声音了),
BeanShell被用作流程跳转的Parser,举个例子,比如一个流程A节点的下一个节点是B或者C,
如何决定A->B还是A->C呢,我们可以写一段Java脚本放在流程定义里面,
一旦A运行完毕,流程会使用BeanShell来Parse这段Java脚本,
根据脚本的返回值决定流程下一步的运行方向,
脚本在这里虽然用得不多,但是却是非常的重要,
我们知道,简单的表达式是远远满足不了流程跳转的判断的,
有了脚本就可以满足任何不规则的业务逻辑。
继以上说到的,使用BeanShell可以处理现实中不规则的业务,举个很典型的例子,
我们知道,一个保险公司有很多险种,每个险种的投入和收益的算法是很复杂的,
无法用规则的关系数据库模型来描述,所以很多保险系统在处理险种的算法都是硬编码,
如果要在系统中新加一个险种,是非常麻烦的,重新修改代码,重新发布,
这对开发\维护人员和客户都是很痛苦的,有了BeanShell,我们可以从这种痛苦中解脱出来,
对每个险种使用不同的脚本,新加的险种我们新配一个脚本,这样既可以满足业务多变的要求,
又可以减少工作量,节约成本。
BeanShell的一个特点是动态执行Java代码,脚本文件改变不会影响当前脚本的调用,
新脚本将在脚本的下一次调用生效,这不同于配置文件,配置文件改变一般都需要应用重启。
下面举个例子来说明BeanShell的使用(JDK1.5,BeanShell2.04),
主类:
package com.test.BeanShell
import bsh.Interpreter
public class TestShell {
public static void main(String[] args) throws Exception {
Interpreter interpreter = new Interpreter()
interpreter.set("inValue", new Integer(1))
interpreter.source("/D://BeanShell_test/test.bsh")
System.out.println(((Integer) interpreter.get("outValue")).intValue())
}
}
脚本文件(D:/BeanShell_test/test.bsh)中的内容为::
System.out.println("hello,in value is "+inValue)
outValue = inValue+1
结果:
hello,in value is 1
2
再来一个简单例子:
(1) 下载BeanShell的jar,加到 classpath 里
(2) 写一个脚本文件 myscript.txt ,内容如下:
a = 2
b = 3
c = a + b
print(c)
(3) 运行该脚本
d:\BeanShell>java bsh.Interpreter myscript.txt
如果没有将BeanShell的JAR加到classpath路径里,运行下面的命令:
java -classpath %CLASSPATH%bsh-2.0b4.jar bsh.Interpreter myscript.txt
OK,输出结果5
写一个bat文件 bsh.bat放到系统目录下,如:winnt 文件内容如下:
java bsh.Interpreter %1
就可以使用如下方法运行脚本文件了
c:\BeanShell>bsh myscript.txt
osworkflow用的就是他了
和Eclipse的集成: http://eclipse-shell.sourceforge.net/index.html
eclipse update url: http://eclipse-shell.sourceforge.net/update/
有用的BeanShell命令:
在刚才那个例子中我们用了一个内建在BeanShell中的一个方便的命令print(),来显示变量的值。
print()跟Java的System.out.println()非常的相像,除非它能保证输出总是命令行。
print()也可以显示一些对象的类型(如数组),但比Java的更详细。
另一个相关的命令是show(),用来开启与关闭显示你输入的每一行的结果。
没错,类也是对象irb(main):001:0>String.class
=>Class
irb(main):002:0>Class.class
=>Class
irb(main):003:0>Class.superclass
=>Module
irb(main):004:0>