前言
最近一直在研究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类型,Pyjnius使用Java反射提供一个新的autoclass()。
1. System = autoclass('java.lang.System') 2. System
System.out
递归反射可以提供一个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')
当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