您的位置:首页 > 编程语言 > Java开发

thinking in java 附录A 使用非JAVA代码

2008-01-23 14:46 302 查看
2006-01-07 01:32:00





附录A 使用非Java代码
A.1 Java固有接口
A.1.1 调用固有方法
A.1.2 访问JNI函数:JNIEnv自变量
A.1.3 传递和使用Java对象
A.1.4 JNI和Java违例
A.1.5 JNI和线程处理
A.1.6 使用现成代码
A.2 微软的解决方案
A.3 J/Direct
A.3.1 @dll.import引导命令
A.3.2 com.ms.win32包
A.3.3 汇集
A.3.4 编写回调函数
A.3.5 其他J/Direct特性
A.4 本原接口(RNI)
A.4.1 RNI总结
A.5 Java/COM集成
A.5.1 COM基础
A.5.2 MS Java/COM集成
A.5.3 用Java设计COM服务器
A.5.4 用Java设计COM客户
A.5.5 ActiveX/Beans集成
A.5.6 固有方法与程序片的注意事项
A.6 CORBA
A.6.1 CORBA基础
A.6.2 一个例子
A.6.3 Java程序片和CORBA
A.6.4 比较CORBA与RMI
A.7 总结

附录A 使用非JAVA代码

JAVA语言及其标准API(应用程序编程接口)应付应用程序的编写已绰绰有余。但在某些情况下,还是必须使用非JAVA编码。例如,我们有时要访问操作系统的专用特性,与特殊的硬件设备打交道,重复使用现有的非Java接口,或者要使用“对时间敏感”的代码段,等等。与非Java代码的沟通要求获得编译器和“虚拟机”的专门支持,并需附加的工具将Java代码映射成非Java代码(也有一个简单方法:在第15章的“一个Web应用”小节中,有个例子解释了如何利用标准输入输出同非Java代码连接)。目前,不同的开发商为我们提供了不同的方案:Java 1.1有“Java固有接口”(Java Native Interface,JNI),网景提出了自己的“Java运行期接口”(Java Runtime Interface)计划,而微软提供了J/Direct、“本源接口”(Raw Native Interface,RNI)以及Java/COM集成方案。
各开发商在这个问题上所持的不同态度对程序员是非常不利的。若Java应用必须调用固有方法,则程序员或许要实现固有方法的不同版本——具体由应用程序运行的平台决定。程序员也许实际需要不同版本的Java代码,以及不同的Java虚拟机。
另一个方案是CORBA(通用对象请求代理结构),这是由OMG(对象管理组,一家非赢利性的公司协会)开发的一种集成技术。CORBA并非任何语言的一部分,只是实现通用通信总线及服务的一种规范。利用它可在由不同语言实现的对象之间实现“相互操作”的能力。这种通信总线的名字叫作ORB(对象请求代理),是由其他开发商实现的一种产品,但并不属于Java语言规范的一部分。
本附录将对JNI,J/DIRECT,RNI,JAVA/COM集成和CORBA进行概述。但不会作更深层次的探讨,甚至有时还假定读者已对相关的概念和技术有了一定程度的认识。但到最后,大家应该能够自行比较不同的方法,并根据自己要解决的问题挑选出最恰当的一种。

A.1 Java固有接口
JNI是一种包容极广的编程接口,允许我们从Java应用程序里调用固有方法。它是在Java 1.1里新增的,维持着与Java 1.0的相应特性——“固有方法接口”(NMI)——某种程度的兼容。NMI设计上一些特点使其未获所有虚拟机的支持。考虑到这个原因,Java语言将来的版本可能不再提供对NMI的支持,这儿也不准备讨论它。
目前,JNI只能与用C或C++写成的固有方法打交道。利用JNI,我们的固有方法可以:
■创建、检查及更新Java对象(包括数组和字串)
■调用Java方法
■俘获和丢弃“异常”
■装载类并获取类信息
■进行运行期类型检查
所以,原来在Java中能对类及对象做的几乎所有事情在固有方法中同样可以做到。

A.1.1 调用固有方法
我们先从一个简单的例子开始:一个Java程序调用固有方法,后者再调用Win32的API函数MessageBox(),显示出一个图形化的文本框。这个例子稍后也会与J/Direct一志使用。若您的平台不是Win32,只需将包含了下述内容的C头:
#include >windows.h<
替换成:
#include >stdio.h<
并将对MessageBox()的调用换成调用printf()即可。
第一步是写出对固有方法及它的自变量进行声明的Java代码:

999页程序

在固有方法声明的后面,跟随有一个static代码块,它会调用System.loadLibrary()(可在任何时候调用它,但这样做更恰当)System.loadLibrary()将一个DLL载入内存,并建立同它的链接。DLL必须位于您的系统路径,或者在包含了Java类文件的目录中。根据具体的平台,JVM会自动添加适当的文件扩展名。

1. C头文件生成器:javah
现在编译您的Java源文件,并对编译出来的.class文件运行javah。javah是在1.0版里提供的,但由于我们要使用Java 1.1 JNI,所以必须指定-jni参数:
javah -jni ShowMsgBox
javah会读入类文件,并为每个固有方法声明在C或C++头文件里生成一个函数原型。下面是输出结果——ShowMsgBox.h源文件(为符合本书的要求,稍微进行了一下修改):

1000页程序

从“#ifdef_cplusplus”这个预处理引导命令可以看出,该文件既可由C编译器编译,亦可由C++编译器编译。第一个#include命令包括jni.h——一个头文件,作用之一是定义在文件其余部分用到的类型;JNIEXPORT和JNICALL是一些宏,它们进行了适当的扩充,以便与那些不同平台专用的引导命令配合;JNIEnv,jobject以及jstring则是JNI数据类型定义。

2. 名称管理和函数签名
JNI统一了固有方法的命名规则;这一点是非常重要的,因为它属于虚拟机将Java调用与固有方法链接起来的机制的一部分。从根本上说,所有固有方法都要以一个“Java”起头,后面跟随Java方法的名字;下划线字符则作为分隔符使用。若Java固有方法“过载”(即命名重复),那么也把函数签名追加到名字后面。在原型前面的注释里,大家可看到固有的签名。欲了解命名规则和固有方法签名更详细的情况,请参考相应的JNI文档。

3. 实现自己的DLL
此时,我们要做的全部事情就是写一个C或C++源文件,在其中包含由javah生成的头文件;并实现固有方法;然后编译它,生成一个动态链接库。这一部分的工作是与平台有关的,所以我假定读者已经知道如何创建一个DLL。通过调用一个Win32 API,下面的代码实现了固有方法。随后,它会编译和链接到一个名为MsgImpl.dll的文件里:

1001页程序

若对Win32没有兴趣,只需跳过MessageBox()调用;最有趣的部分是它周围的代码。传递到固有方法内部的自变量是返回Java的大门。第一个自变量是类型JNIEnv的,其中包含了回调JVM需要的所有挂钩(下一节再详细讲述)。由于方法的类型不同,第二个自变量也有自己不同的含义。对于象上例那样的非static方法(也叫作实例方法),第二个自变量等价于C++的“this”指针,并类似于Java的“this”:都引用了调用固有方法的那个对象。对于static方法,它是对特定Class对象的一个引用,方法就是在那个Class对象里实现的。
剩余的自变量代表传递到固有方法调用里的Java对象。主类型也是以这种形式传递的,但它们进行的“按值”传递。
在后面的小节里,我们准备讲述如何从一个固有方法的内部访问和控制JVM,同时对上述代码进行更详尽的解释。

A.1.2 访问JNI函数:JNIEnv自变量
利用JNI函数,程序员可从一个固有方法的内部与JVM打交道。正如大家在前面的例子中看到的那样,每个JNI固有方法都会接收一个特殊的自变量作为自己的第一个参数:JNIEnv自变量——它是指向类型为JNIEnv_的一个特殊JNI数据结构的指针。JNI数据结构的一个元素是指向由JVM生成的一个数组的指针;该数组的每个元素都是指向一个JNI函数的指针。可从固有方法的内部发出对JNI函数的调用,做法是撤消对这些指针的引用(具体的操作实际很简单)。每种JVM都以自己的方式实现了JNI函数,但它们的地址肯定位于预先定义好的偏移处。
利用JNIEnv自变量,程序员可访问一系列函数。这些函数可划分为下述类别:
■获取版本信息
■进行类和对象操作
■控制对Java对象的全局和局部引用
■访问实例字段和静态字段
■调用实例方法和静态方法
■执行字串和数组操作
■产生和控制Java异常
JNI函数的数量相当多,这里不再详述。相反,我会向大家揭示使用这些函数时背后的一些基本原理。欲了解更详细的情况,请参阅自己所用编译器的JNI文档。
若观察一下jni.h头文件,就会发现在#ifdef _cplusplus预处理器条件的内部,当由C++编译器编译时,JNIEnv_结构被定义成一个类。这个类包含了大量内嵌函数。通过一种简单而且熟悉的语法,这些函数让我们可以从容访问JNI函数。例如,前例包含了下面这行代码:
(*jEnv)-
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: