在某些Java的忠实支持者眼中,JNI(Java Native Interface)是难登大雅之堂的,因为JNI生来便仿佛和Java“Write Once, Run Everywhere”的宗旨相背离。其实这是一种偏见,因为只要承认Java在执行一些关键业务时在实时性方面的不足,只要承认Java在与底层交互方面存在诸多壁垒,JNI便有其存在的必要。况且,更理性地看待这个问题:Java最初诞生的机缘在于网络的兴起,而后来流行的原因则在于掌握企业计算命脉的几个关键厂商的推波助澜,这一切都决定了Java擅长于企业应用开发,而在实时应用和桌面应用领域的不足。正如同无人能够抹煞Java的安全性和组件化,其他语言自有其存在和发展的理由。JNI正是为Java和其他语言之间的互操作打开了方便之门,可以说,这对Java团队来说是借力打力的好事!
另一种顾虑来自于“耦合度”方面,部分人认为实现异构系统、异质语言之间的互操作途径有很多种,未必需要基于JNI重写系统。笔者认为:(1)系统的耦合度首先不在于技术路线的选型,而在于设计和规划:即使采用SOA(面向服务的体系结构)和ESB(企业服务总线),一个不良的设计,很可能让实施效果大打折扣;(2)应用系统的需求是多方面的,耦合度只是其中之一,而在耦合度之外,还有性能和可靠性等指标:我们不能接受,也难以想象每一次原本可以通过本地调用完成的任务,都要发起一次进程间调用。
本章主要内容包括:
¿ JNI原理
¿ 调用C程序
¿ 调用Delphi程序
本章首先介绍JNI原理,然后分别介绍两种JNI的应用场景:Java语言调用C程序、Java语言调用Delphi程序。
15.1 JNI原理JNI是Java Native Interface(Java本地接口)的缩写。所谓本地接口,是相对运行在Java虚拟机“沙箱”中的Java程序而言的,指直接运行在操作系统之上,与操作系统直接交互的程序。从JDK 1.1开始,JNI规范成为Java平台的一部分,它允许Java程序和用其他语言编写的程序进行交互。JNI一开始是为了Java程序与本地已编译语言,尤其是C和C++的互操作而设计的,但是这并不意味着不能使用其他语言。
使用Java语言与本地已编译的代码交互,意味着丧失平台Java语言“Write Once, Run Everywhere”的可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。比如,使用一些旧的类库与硬件、操作系统进行交互,或者为了提高程序的性能。JNI规范保证了Java程序能够以尽量少的代码、尽可能相同的方式调用本地类库。
可以形象直观地把JNI看做是Java和本地应用程序的黏合剂。如图15-1所示,JNI规范成为沟通C语言类库及函数和Java对象之间的媒介。
图15-1 JNI原理
一般来说,使用100%纯Java语言编写程序仍然是首选目标,毕竟程序的可移植性和安全性是我们选择Java语言的初衷。概括起来,采用JNI调用本地程序接口的情况包括以下几个方面。
— 为了实现Java语言所不能实现的功能:例如关闭系统、获取CPU繁忙程度等。
— 为了获取更好的程序运行性能:和其他语言一样,Java程序运行的性能很大程度上取决于程序代码的编写质量,但是在一些实时性要求高的应用场合,以“半编译”形态出现的Java语言是难以胜任的,这时借助运行性能更佳的本地程序来完成这些关键任务更为合理。
— 为了保护历史投资:当然前提条件是程序的可移植性不是首要被看重的因素。企业应用开发成本往往是决定编码规模和开发周期的重要因素,当已经有一些使用传统语言编写的模块被证明能够良好地实现企业业务需求,为了缩短开发周期、降低开发成本,可能就需要考虑采用JNI调用这些旧模块。
指出一点,使用Java虚拟机运行时Java LangRuntime调用外部进程不属于JNI的范畴。关于Java语言如何调用外部进程,请参考本书的第6章“输入输出综合”的有关内容。
在下一节中,我们将介绍使用JNI调用C程序的典型步骤。