(重定向自 Develop.JPype)
On this page... (hide)
- 1. 基本安装
- 2. 启动JVM
- 3. 如何调用一个Java函数
- 4. 如果捕捉Java异常
- 5. 如何处理Java的函数多态
- 6. 如何重启JVM
- 7. 关于多线程及Python实现Java接口等
在Python中调用Java类库和Java代码的桥接解决方案,背后使用的机制是JNI。虽然看起来这个库近年来更新不够活跃,原作者也试图用一个叫JEmbed(还没有发布)来替代之,但就我个人的使用体会来讲,现有版本是稳定可用的。
感谢Limodou~正是当初搜到了他在ChinaUnix上的旧帖子,才知道了jpype~
- 官方网站:http://jpype.sourceforge.net/
- 官方使用文档:http://jpype.sourceforge.net/doc/user-guide/userguide.html
1. 基本安装
经试验,0.5.3版本不需要特殊处理,直接python setup.py install就可以了(安装过程中显示的warning可以不必理会)。用于测试是否安装成功的Hello World代码如下:
startJVM(getDefaultJVMPath())
java.lang.System.out.println("hello world")
shutdownJVM()
2. 启动JVM
依靠startJVM这个函数来完成,一个使用的例子是这样的:
jpype.startJVM(vmPath, "-Xms32m", "-Xmx256m", "-mx256m", "-Djava.class.path=/home/some-lib.jar:")
startJVM的第一个参数是JVM库所在的路径(和JAVA_HOME不是一回事儿),通常可以用jpype.getDefaultJVMPath()来自动获取系统默认JVM的路径。如果系统中安装了多个JDK,希望从中选择一个,则可以手动注明这个路径。比如Mac OSX下可以写成"/System/Library/Frameworks/JavaVM.framework/Libraries/libjvm_compat.dylib"。
剩下的都是发送给JVM的启动参数,每个逗号见是一个参数。因为这里是不支持带空格的参数写法的,所以例子里特意把classpath参数写成了-Djava.class.path=...的形式。注意这里需要手工保证参数的正确性,jpype是不会对错误的参数给出提示的,它的反应很简单,就是在后面用到这个JVM的时候报一些怎么也想不明白的错误……所以,使用jpype遇到任何问题,首先检查传给startJVM的各参数正确性。
3. 如何调用一个Java函数
主要靠JPackage语句来实现,比如
java和javax两个包不需要以这种方式来调用,直接类似jpype.java.lang.System.out.println()这样就可以了。
有时候我们会遇到类似"TypeError: Package org.w3c.dom.Document is not Callable"这样的错误。通常这时用到的Java指令在jar里面,而这个jar没有被正确导入,所以JVM找不到它。也就是说,遇到这种错误时,要去检查startJVM函数中的-Djava.class.path=参数的设置,通常都是因为这里的路径写错了造成的。
4. 如果捕捉Java异常
可以在Python里使用 jpype.JavaException指代所有的Java异常,比如像下面这样:
jpype.startJVM(jpype.getDefaultJVMPath())
try:
jpype.java.lang.Integer("x1")
except jpype.JavaException, ex:
print ex.javaClass(), ex.message()
print ex.stacktrace()
jpype.shutdownJVM()
如果要捕获特定的Java异常呢,则需要用到jpype.JException,比如像下面这样捕获的就是java.lang.NumberFormatException:
jpype.startJVM(jpype.getDefaultJVMPath())
try:
jpype.java.lang.Integer("x1")
except jpype.JException(jpype.java.lang.NumberFormatException), ex:
print ex.javaClass(), ex.message()
print ex.stacktrace()
jpype.shutdownJVM()
5. 如何处理Java的函数多态
Java里面是允许参数格式不同的多个同名函数的,Python里面则不允许。这样在通过jpype调用Java api里面的函数时,有时会因为参数的类型乱掉而报错。那么怎么能调用到Java里面的特定函数呢?没办法,做强制类型转换吧。
比如jpype.java.lang.System.out.println(1)实际会调用println(int),那么如果我们想调用println(byte),则可以写成jpype.java.lang.System.out.println(JByte(1))这样。。
6. 如何重启JVM
jpype提供的shutdownJVM()方法实际调用的是JNI接口的unload实现,但是Sun对unload的实现有点问题,造成的结果就是jpype调用shutdownJVM()以后就没法再startJVM()了(会报错)。什么?您问关掉JVM干嘛还要重新开启它,这个折腾个什么劲?答案很简单:因为有时在未知的黑暗角落隐藏着邪恶的源头——内存泄露。。
既然jpype没法重启JVM,那么只好把jpype放到processing里面来用,需要重启时,就杀掉当前进程,重新启动一个新进程好了。。(processing安装很简单,Python 2.6官方发行版已经带了,之前的版本则可以easy_install processing)
下面给出一个processing下用jpype的例子:
import processing
def java_loop(pipe, id):
jpype.startJVM(jpype.getDefaultJVMPath())
while True:
jpype.java.lang.System.out.println(pipe.recv() + ' ' + id)
pipe.send(None)
head1, head2 = processing.Pipe()
p = processing.Process(target = java_loop, args = [head2, '(JVM 1)'])
p.setDaemon(True)
p.start()
head1.send("Hello message from")
head1.recv()
p.terminate()
head1, head2 = processing.Pipe()
p = processing.Process(target = java_loop, args = [head2, '(JVM 2)'])
p.setDaemon(True)
p.start()
head1.send("Hello message from")
head1.recv()
这里用到的pipe.recv()要小心,一旦阻塞可能会与twisted之类的框架产生冲突。一个可能的解决办法是在recv()之前用pipe.poll()函数检测一下管道里面是否有待接收数据,如果没有就等一会重新poll()就是了。(poll()自称是非阻塞的,因为它只阻塞当前线程;而recv()则会阻塞当前进程,于是twisted就不干了^_^)
7. 关于多线程及Python实现Java接口等
这些我还没用到……所以,参考jpype官方文档吧……