前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >一文速学-Python联通调用JAVA的桥梁PyJnius库详解

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

原创
作者头像
fanstuck
发布2024-12-28 19:57:16
发布2024-12-28 19:57:16
31600
代码可运行
举报
运行总次数:0
代码可运行

前言

最近一直在研究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命令提示符里面输入:

代码语言:bash
复制
pip install pyjnius

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

代码语言:bash
复制
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyjnius

conda的下载:

代码语言:bash
复制
conda install -c conda-forge pyjnius

方法二

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

pyjnius-1.4.2-cp37-cp37m-win32.whl

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

2.相关依赖

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

代码语言:bash
复制
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就没有事。

代码语言:python
代码运行次数:0
复制
from jnius import autoclass

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

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

代码语言:python
代码运行次数:0
复制
System = autoclass('java.lang.System')
System
代码语言:python
代码运行次数:0
复制
System.out

三、Reflection类

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

类似这种形式:

代码语言:python
代码运行次数:0
复制
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类的反射如下所示:

代码语言:python
代码运行次数:0
复制
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格式。

代码语言:python
代码运行次数:0
复制
from jnius import autoclassQ
autoclass('java.lang.System')

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

代码语言:python
代码运行次数:0
复制
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方法。

代码语言:python
代码运行次数:0
复制
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方法名,仍然可以强制执行。

代码语言:python
代码运行次数:0
复制
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签名有一种特殊的格式,一开始可能很难理解。让我们看看细节。签名的格式为:

代码语言: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或空。

代码语言:java
复制
(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类的签名。例如:

代码语言: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启动后无法更改。为此,可以:

代码语言:python
代码运行次数:0
复制
#antlrtest.py
import jnius_config
jnius_config.add_options('-Xms4096m')
jnius_config.set_classpath('./','./jar_package/antlr-3.5.2-complete.jar')
import jnius

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

完整使用案例:

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

代码语言:python
代码运行次数:0
复制
#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())
代码语言:bash
复制
select
 
*
 
from
 
new_table
;
<EOF>

Process finished with exit code 0

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 1.下载方式
      • 方法一
      • 方法二
    • 2.相关依赖
  • 二、使用测试
  • 三、Reflection类
  • 四、Reflection函数
    • 1.jnius.autoclass(name)
    • Python中的Java类实现
    • JVM选项和类路径
    • 完整使用案例:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档