JPype is an effort to allow python programs full access to java class libraries. This is achieved not through re-implementing Python, as Jython/JPython has done, but rather through interfacing at the native level in both Virtual Machines.
JPype是一个高效的python库,它允许python程序和java类互通。它并非是一个重新实现的Python,就像Jython和JPython那样,而是通过在虚拟机native层面上实现。
Eventually, it should be possible to replace Java with python in many, though not all, situations. JSP, Servlets, RMI servers and IDE plugins are all good candidates.
实际上,它应该可以在许多的场景下使用python来替换Java,当然了并非所有的都能替换。像 JSP, Servlets, RMI servers and IDE plugins都是很好的替换对象。
—————————————–强行分割一下吧—————————————-
最近学习写一些用来处理日志log的python脚本,但是到连接到线上数据库的时候出现了点小小的麻烦,因为众所周知,renren的代码是java的,连接数据库都被java做了封装,不能显示的看到具体那台数据库【实际上开发者也无需关心,我们只需要一个配置文件,具体指向什么地方,后面是一台机器还是多台,都不在操心。】就想找到一个python能直接访问java程序的解决方案,翻来翻去就找到JPype了,它的源码好久没更新,不过最近更新了一下,最新的版本是0.5.4.1,可以通过这个链接下载。
安装很简单,直接直接python setup.py install就可以了(安装过程中显示的warning可以不必理会)。用于测试是否安装成功的Hello World代码如下:
from jpype import *
startJVM(getDefaultJVMPath())
java.lang.System.out.println("hello world")
shutdownJVM()
运行结果如下:
[root@SJSWT45-26 wei]# python testp2j.py
hello world
JVM activity report :
classes loaded : 30
JVM has been shutdown
2. 启动JVM
依靠startJVM这个函数来完成,一个使用的例子是这样的:
vmPath = jpype.getDefaultJVMPath()
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语句来实现,比如
Document = jpype.JPackage('org').w3c.dom.Document
可以把Java里面的org.w3c.dom.Document映射给Python里面的Document变量。
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异常,比如像下面这样:
import jpype
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:
import jpype
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 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就不干了]