一文速学-Python联通调用JAVA的桥梁PyJnius库详解

2年前 (2022) 程序员胖胖胖虎阿
574 0 0

目录

前言

一、PyJnius

1.下载方式

方法一

 方法二

2.相关依赖

二、使用测试

三、Reflection类

四、Reflection函数

1.jnius.autoclass(name)

 Python中的Java类实现 

jnius.java_method

Java签名格式 

JVM选项和类路径 

完整使用案例:

点关注,防走丢,如有纰漏之处,请留言指教,非常感谢


前言

最近一直在研究HiveSQL的源码以及ANTLR包的源码,比较无奈的是工程上我还是偏向于使用Pycharm和python编程语言。其实编程语言选择都无所谓只是工具罢了,主要的是其中解析抽象树AST的思想以及方法。但是基础的语法方法需要掌握,比如Python调库以及引用,JAVA的import规则以及jar包的引用。要做成工程化的程序,程序员就必须有一定的工具使用能力,比如anaconda和IDEA的基础使用方法,做HiveSQL血缘分析的时候遇到了很多大坑以及众多BUG报错,对于自身代码能力和解决问题的能力也有一定的成长。好了废话不多说,就让我们来研究如何来使用该库实现相应功能吧。博主将长期维护自己博客的文章,如有披露错误或者不理解之处请尽情在评论区留下发言。希望能够帮助到需要掌握该库的各位。


一、PyJnius

PyJnius库正如文章标题,是一个用于访问Java类的Python库。PyJnius官网:Welcome to Pyjnius — Pyjnius 1.0a1 documentation

github:

GitHub - kivy/pyjnius: Access Java classes from Python

PyJnius库主要分为三个部分:

  • jnius
  • jnius_config
  • setup_sdist

现在的PyJnius库的版本为1.4.2。该库通过JVM虚拟机实现调用。

1.下载方式

方法一

直接通过在cmd命令提示符里面输入:

pip install pyjnius

但是这种方式很可能由于连接不稳定失败,建议换个源再下载:

pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyjnius

conda的下载:

conda install -c conda-forge pyjnius

 方法二

直接去Pypi上面下载whl文件也很快,毕竟现在连外网很不稳定,网速很慢。大家可以在本人资源列表上面下载whl匹配版本的文件:

pyjnius-1.4.2-cp37-cp37m-win32.whl-Python文档类资源-CSDN下载

下载whl文件之后进入cmd上面cd到当前下载的目录下面,pip该文件就好了。

2.相关依赖

由于是调用的JAVA的Class类那肯定需要的依赖比较多,需要安装cython这个库,如果大家有装acaconda的话去环境里面下载就好了,而pyjnius在anaconda里面是没有的,也不知道是不是我版本太低了没有找到。当然网络连接稳定的直接pip就好了,接连的话要在下载一个gcc编译器:

yum install gcc gcc-++
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple cython

 当然自然需要JAVA环境了,需要大家自行下载JDK,我相信这个是个程序员应该都装过,那么JAVA_HOME应该都配置过了,但是这里需要再额外配置一下环境变量PATH,需要找到jvm.dll:

C:\Program Files\Java\jdk1.7.0_79\jre\bin\server 

其中java的中的bin目录下server里面就有java的jvm.dll文件了。

二、使用测试

我们可以试试调用JAVA自身的一些类来使用,就类似于python自带的原始库。我建议是如果大家想要使用其他的jar包的话最好是使用pycharm来实现,Jupyter是我主要用的一个编译器但是使用jnius的话config设置路径的话会报错,如果使用pycharm就没有事。

from jnius import autoclass

Stack = autoclass('java.util.Stack')
stack = Stack()
stack.push('hello world')

一文速学-Python联通调用JAVA的桥梁PyJnius库详解

 如果返回类型不是Python类型,Pyjnius使用Java反射提供一个新的autoclass()。

System = autoclass('java.lang.System')
System

一文速学-Python联通调用JAVA的桥梁PyJnius库详解

System.out

一文速学-Python联通调用JAVA的桥梁PyJnius库详解

 递归反射可以提供一个Reflection返回的Java对象的适当对象。

三、Reflection类

用于反映Java类的基。其思想是对这个JavaClass进行子类化,添加一些JavaMethod、JavaStaticMethod,JavaField和JavaStaticField。

类似这种形式:

from jnius import JavaClass, MetaJavaClass

class Stack(JavaClass):
    __javaclass__ = 'java/util/Stack'
    __metaclass__ = MetaJavaClass
  • __metaclass__: 必须设置为MetaJavaClass,否则,声明的所有方法/字段将不会链接到JavaClass。
  • __javaclass__:表示Java类名,格式为“org/lang/class”(例如“Java/util/Stack”),而不是“org.lang.class”。
  • __javaconstructor__:如果未设置,假设默认构造函数不带参数。否则,它可以是构造函数的所有可能签名的列表。

例如,字符串java类的反射如下所示:

class String(JavaClass):
    __javaclass__ == 'java/lang/String'
    __metaclass__ = MetaJavaClass
    __javaconstructor__ == (
        '()V',
        '(Ljava/lang/String;)V',
        '([C)V',
        '([CII)V',
        # ...
    )

 

还有更多类就不一一详说了,核心类就这三个,更多的类都是基于此衍生出来的

四、Reflection函数

1.jnius.autoclass(name)

返回表示从name传递的类的JavaClass。名称必须采用a.b.c格式,而不是a/b/c格式。

from jnius import autoclassQ
autoclass('java.lang.System')

 一文速学-Python联通调用JAVA的桥梁PyJnius库详解

当JAVA出现了Python的关键字时(例如from、class等)的成员。需要使用getattr()来访问该成员,然后才能调用它:

from jnius import autoclass
func_from = getattr(autoclass('some.java.Class'),'from')
func_from()

 SomeClass还有一个特例。类文本,可以将其作为SomeClass的结果找到。getClass()或__javaclass__ python属性中。

 Python中的Java类实现 

从Python类创建Java类的基础。可以完全用Python实现java接口。
实际上,将创建一个Python类,它模仿声明的__javainterfaces__列表。当将这个类的实例提供给Java时,Java将只接受它并调用声明的接口方法。在幕后,我们正在捕获调用,并将其重定向到使用声明的Python方法。创建的类将充当Java接口的代理。
但是需要至少定义__javainterfaces__属性,并使用java_method()修饰符声明java方法。

from jnius import PythonJavaClass, java_method

class PythonListIterator(PythonJavaClass):
    __javainterfaces__ = ['java/util/ListIterator']

    def __init__(self, collection, index=0):
        super(TestImplemIterator, self).__init__()
        self.collection = collection
        self.index = index

    @java_method('()Z')
    def hasNext(self):
        return self.index < len(self.collection.data) - 1

    @java_method('()Ljava/lang/Object;')
    def next(self):
        obj = self.collection.data[self.index]
        self.index += 1
        return obj
  • __javainterfaces__:要代理的Java接口列表,格式为“org/lang/Class”(例如“Java/util/Iterator”),而不是“org.lang.Class”。
  • __javacontext__:指示要使用的类加载器,“系统”或“应用程序”。默认值为“系统”。

jnius.java_method

与PythonJavaClass一起使用的装饰函数。java_signature必须与接口的所需签名匹配。默认情况下,方法的名称将是Python方法的名称。如果多个签名具有相同的Java方法名,仍然可以强制执行。

class PythonListIterator(PythonJavaClass):
    __javainterfaces__ = ['java/util/ListIterator']

    @java_method('()Ljava/lang/Object;')
    def next(self):
        obj = self.collection.data[self.index]
        self.index += 1
        return obj

Java签名格式 

Java签名有一种特殊的格式,一开始可能很难理解。让我们看看细节。签名的格式为:

(<argument1><argument2><...>)<return type>

 签名任何部分的所有类型都可以是以下类型之一:

  • L<java class>; = represent a Java object of the type <java class>
  • Z = represent a java/lang/Boolean;
  • B = represent a java/lang/Byte;
  • C = represent a java/lang/Character;
  • S = represent a java/lang/Short;
  • I = represent a java/lang/Integer;
  • J = represent a java/lang/Long;
  • F = represent a java/lang/Float;
  • D = represent a java/lang/Double;
  • V = represent void, available only for the return type

所有类型都可以具有[前缀以指示数组。返回类型可以是V或空。

(ILjava/util/List;)V
-> argument 1 is an integer
-> argument 2 is a java.util.List object
-> the method doesn't return anything.

(java.util.Collection;[java.lang.Object;)V
-> argument 1 is a Collection
-> argument 2 is an array of Object
-> nothing is returned

([B)Z
-> argument 1 is a Byte []
-> a boolean is returned

 在Python中实现Java时,Java方法的签名必须匹配。Java提供了一个名为javap的工具来获取任何Java类的签名。例如:

$ javap -s java.util.Iterator
Compiled from "Iterator.java"
public interface java.util.Iterator{
public abstract boolean hasNext();
  Signature: ()Z
public abstract java.lang.Object next();
  Signature: ()Ljava/lang/Object;
public abstract void remove();
  Signature: ()V
}

JVM选项和类路径 

在调用导入JNIU之前需要设置JVM选项,因为它们在VM启动后无法更改。为此,可以:

#antlrtest.py
import jnius_config
jnius_config.add_options('-Xms4096m')
jnius_config.set_classpath('./','./jar_package/antlr-3.5.2-complete.jar')
import jnius

其中 :

jnius_config.add_options():此选项为Jvm参数:

  • -Xms4096m:初始堆内存4g
  • -Xmx4096m:最大堆内存4g
  • -Xmn1024m:年轻代1g
  • -Xss256K:每个线程占用的空间
  • -XX:+DisableExplicitGC:禁止显示调用gc
  • -XX:MaxTenuringThreshold=15:在年轻代存活次数
  • -XX:+UseParNewGC:对年轻代采用多线程并行回收
  • -XX:+UseConcMarkSweepGC:年老代采用CMS回收
  • -XX:+CMSParallelRemarkEnabled:在使用UseParNewGC 的情况下, 尽量减少 mark 的时间
  • -XX:+UseCMSCompactAtFullCollection:在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少
  • -XX:LargePageSizeInBytes=128m:指定 Java heap的分页页面大小
  • -XX:+UseFastAccessorMethods:get,set 方法转成本地代码
  • -XX:+UseCMSInitiatingOccupancyOnly:指示只有在 oldgeneration 在使用了初始化的比例后concurrent collector 启动收集
  • -XX:CMSInitiatingOccupancyFraction=70:年老代到达70%进行gc
  • -Djava.awt.headless=true :Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
  • -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/gclogs/gc.log:打印日志信息

jnius_config.set_classpath():JAVAclassPATH,路径可为想要运行的所有jar包。

如果使用这些函数设置了类路径,它将覆盖任何类路径环境变量。应将多个选项或路径条目作为多个参数提供给add_和set_函数。如果未提供类路径且未设置类路径,则路径默认为“.”。此功能在Android上不可用。

完整使用案例:

pyjnius库的实际内容没有多少,主要是桥梁作用,这里放上一段使用pyjnius来调用JAVAjar包的实际案例:

#antlrtest.py
import jnius_config
jnius_config.set_classpath('./','./grammar/hive310/antlr-3.5.2-complete.jar')
import jnius
StringStream = jnius.autoclass('org.antlr.runtime.ANTLRStringStream')
Lexer  = jnius.autoclass('grammar.hive310.HiveLexer')
TokenStream  = jnius.autoclass('org.antlr.runtime.CommonTokenStream')

cstream = StringStream("select * from new_table;")
inst = Lexer(cstream)
ts = TokenStream()
ts.setTokenSource(inst)
ts.fill()

jlist = ts.getTokens()
tsize = jlist.size()
for i in range(tsize):
    print(jlist.get(i).getText())

select
 
*
 
from
 
new_table
;
<EOF>

Process finished with exit code 0


 

点关注,防走丢,如有纰漏之处,请留言指教,非常感谢

以上就是本期全部内容。我是fanstuck ,有问题大家随时留言讨论 ,我们下期见。

相关文章

暂无评论

暂无评论...