Java编程思想第4版[中文版](PDF格式)-第170部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
注册表的一项重要特点就是它作为客户和服务器对象之间的一个去耦层使用。利用注册表内保存的一些信
息,客户会激活服务器;其中一项信息是服务器执行模块的物理位置。若这个位置发生了变动,注册表内的
信息就会相应地更新。但这个更新过程对于客户来说是“透明”或者看不见的。后者只需直接使用ProgID 或
CLSID 即可。换句话说,注册表使服务器代码的位置透明成为了可能。随着D (分布式)的引入,在
本地机器上运行的一个服务器甚至可移到网络中的一台远程机器,整个过程甚至不会引起客户对它的丝毫注
意(大多数情况下如此)。
2。 类型库
由于 具有动态链接的能力,同时由于客户和服务器代码可以分开独立发展,所以客户随时都要动态侦测
由服务器展示出来的服务。这些服务是用“类型库”(Type Library)中一种二进制的、与语言无关的形式
描述的(就象接口和方法签名)。它既可以是一个独立的文件(通常采用。TLB 扩展名),也可以是链接到执
行程序内部的一种Win32 资源。运行期间,客户会利用类型库的信息调用服务器中的函数。
我们可以写一个Microsoft Interface Definition Language (微软接口定义语言,MIDL )源文件,用MIDL
编译器编译它,从而生成一个。TLB 文件。MIDL 语言的作用是对 类、接口以及方法进行描述。它在名称、
语法以及用途上都类似 OMB/CORBA IDL 。然而,Java 程序员不必使用MIDL 。后面还会讲到另一种不同的
Microsoft 工具,它能读入Java 类文件,并能生成一个类型库。
3。 :HRESULT 中的函数返回代码
由服务器展示出来的 函数会返回一个值,采用预先定义好的HRESULT 类型。HRESULT 代表一个包含了三
个字段的整数。这样便可使用多个失败和成功代码,同时还可以使用其他信息。由于 函数返回的是一个
HRESULT,所以不能用返回值从函数调用里取回原始数据。若必须返回数据,可传递指向一个内存区域的指
针,函数将在那个区域里填充数据。我们把这称为 “外部参数”。作为Java/ 程序员,我们不必过于关注
这个问题,因为虚拟机会帮助我们自动照管一切。这个问题将在后续的小节里讲述。
A。5。2 MS Java/ 集成
同C++/ 程序员相比,Microsoft Java 编译器、虚拟机以及各式各样的工具极大简化了Java/ 程序员
的工作。编译器有特殊的引导命令和包,可将 Java 类当作 类对待。但在大多数情况下,我们只需依赖
Microsoft JVM 为 提供的支持,同时利用两个有力的外部工具。
Microsoft Java Virtual Machine (JVM)在 和Java 对象之间扮演了一座桥梁的角色。若将Java 对象创
建成一个 服务器,那么我们的对象仍然会在 JVM 内部运行。Microsoft JVM 是作为一个 DLL 实现的,它
向操作系统展示出了 接口。在内部,JVM 将对这些 接口的函数调用映射成 Java 对象中的方法调用。
当然,JVM 必须知道哪个Java 类文件对应于服务器执行模块;之所以能够找出这方面的信息,是由于我们事
前已用Javareg 在Windows 注册表内注册了类文件。Javareg 是与Microsoft Java SDK 配套提供的一个工具
程序,能读入一个 Java 类文件,生成相应的类型库以及一个 GUID,并可将类注册到系统内。亦可用 Javareg
注册远程服务器。例如,可用它注册在不同机器上运行的一个服务器。
如果想写一个Java/ 客户,必须经历一系列不同的步骤。Java/ “客户”是一些特殊的Java 代码,它
们想激活和使用系统内注册的一个 服务器。同样地,虚拟机会与 服务器沟通,并将它提供的服务作
为Java 类内的各种方法展示(揭示)出来。另一个Microsoft 工具是 jactivex,它能读取一个类型库,并
生成相应的 Java 源文件,在其中包含特殊的编译器引导命令。生成的源文件属于我们在指定类型库之后命名
的一个包的一部分。下一步是在自己的 客户Java 源文件中导入那个包。
接下来让我们讨论两个例子。
A。5。3 用 Java 设计 服务器
本节将介绍 ActiveX 控件、Automation 服务器或者其他任何符合 规范的服务器的开发过程。下面这个例
子实现了一个简单的Automation 服务器,它能执行整数加法。我们用 setAddend()方法设置addend 的值。
每次调用 sum()方法的时候,addend 就会添加到当前result 里。我们用 getResult()获得result 值,并用
clear()重新设置值。用于实现这一行为的 Java 类是非常简单的:
public class Adder {
private int addend;
663
…………………………………………………………Page 665……………………………………………………………
private int result;
public void setAddend(int a) { addend = a; }
public int getAddend() { return addend; }
public int getResult() { return result; }
public void sum() { result += addend; }
public void clear() {
result = 0;
addend = 0;
}
}
为了将这个 Java 类作为一个 对象使用,我们将 Javareg 工具应用于编译好的Adder。class 文件。这个工
具提供了一系列选项;在这种情况下,我们指定Java 类文件名(〃Adder〃),想为这个服务器在注册表里置
入的ProgID (〃JavaAdder。Adder。1〃 ),以及想为即将生成的类型库指定的名字(〃JavaAdder。tlb〃)。由于
尚未给出CLSID,所以 Javareg 会自动生成一个。若我们再次对同样的服务器调用 Javareg,就会直接使用现
成的CLSID。
javareg /register
/class:Adder /progid:JavaAdder。Adder。1
/typelib:JavaAdder。tlb
Javareg 也会将新服务器注册到 Windows 注册表。此时,我们必须记住将 Adder。class 复制到
WindowsJavatrustlib 目录。考虑到安全方面的原因(特别是涉及程序片调用 服务的问题),只有在
服务器已安装到trustlib 目录的前提下,这些服务器才会被激活。
现在,我们已在自己的系统中安装了一个新的 Automation 服务器。为进行测试,我们需要一个 Automation
控制器,而 Automation 控制器就是 Visual Basic (VB )。在下面,大家会看到几行VB 代码。按照 VB 的格
式,我设置了一个文本框,用它从用户那里接收要相加的值。并用一个标签显示结果,用两个下推按钮分别
调用 sum()和 clear()方法。最开始,我们声明了一个名为Adder 的对象变量。在Form_Load 子例程中(在窗
体首次显示时载入),会调用Adder 自动服务器的一个新实例,并对窗体的文本字段进行初始化。一旦用户
按下“Sum”或者“Clear”按钮,就会调用服务器中对应的方法。
Dim Adder As Object
Private Sub Form_Load()
Set Adder = CreateObject(〃JavaAdder。Adder。1〃)
Addend。Text = Adder。getAddend
Result。Caption = Adder。getResult
End Sub
Private Sub SumBtn_Click()
Adder。setAddend (Addend。Text)
Adder。Sum
Result。Caption = Adder。getResult
End Sub
Private Sub ClearBtn_Click()
Adder。Clear
Addend。Text = Adder。getAddend
Result。Caption = Adder。getResult
End Sub
注意,这段代码根本不知道服务器是用Java 实现的。
664
…………………………………………………………Page 666……………………………………………………………
运行这个程序并调用了 CreateObject()函数以后,就会在Windows 注册表里搜索指定的ProgID。在与
ProgID 有关的信息中,最重要的是Java 类文件的名字。作为一个响应,会启动Java 虚拟机,而且在 JVM 内
部调用Java 对象的实例。从那个时候开始,JVM 就会自动接管客户和服务器代码之间的交流。
A。5。4 用 Java 设计 客户
现在,让我们转到另一侧,并用Java 开发一个 客户。这个程序会调用系统已安装的 服务器内的服
务。就 目前这个例子来说,我们使用的是在前一个例子里为服务器实现的一个客户。尽管代码在Java 程序员
的眼中看起来比较熟悉,但在幕后发生的一切却并不寻常。本例使用了用 Java 写成的一个服务器,但它可应
用于系统内安装的任何 ActiveX 控件、ActiveX Automation 服务器或者 ActiveX 组件——只要我们有一个类
型库。
首先,我们将Jactivex 工具应用于服务器的类型库。Jactivex 有一系列选项和开关可供选择。但它最基本
的形式是读取一个类型库,并生成 Java 源文件。这个源文件保存于我们的windows/java/trustlib 目录中。
通过下面这行代码,它应用于为外部 Automation 服务器生成的类型库:
jactivex /javatlb JavaAdder。tlb
Jactivex 完成以后,我们再来看看自己的windows/java/trustlib 目录。此时可在其中看到一个新的子目
录,名为 javaadder。这个目录包含了用于新包的源文件。这是在 Java 里与类型库的功能差不多的一个库。
这些文件需要使用Microsoft 编译器的专用引导命令:@ 。jactivex 生成多个文件的原因是 使用多个
实体来描述一个 服务器(另一个原因是我没有对MIDL 文件和 Java/ 工具的使用进行细致的调整)。
名为Adder。java 的文件等价于MIDL 文件中的一个 coclass 引导命令:它是对一个 类的声明。其他文件
则是由服务器揭示出来的 接口的 Java 等价物。这些接口(比如Adder_DispatchDefault。java)都属于
“遣送”(Dispatch )接口,属于Automation 控制器与Automation 服务器之间的沟通机制的一部分。
Java/ 集成特性也支持双接口的实现与使用。但是,IDispatch 和双接口的问题已超出了本附录的范围。
在下面,大家可看到对应的客户代码。第一行只是导入由 jactivex 生成的包。然后创建并使用
Automation 服务器的一个实例,就象它是一个原始的 Java 类那样。请注意行内的类型模型,其中“例示”
了 对象(即生成并调用它的一个实例)。这与 对象模型是一致的。在 中,程序员永远不会得到
对整个对象的一个引用。相反,他们只能拥有对类内实现的一个或多个接口的引用。
“例示”Adder 类的一个Java 对象以后,就相当于指示 激活服务器,并创建这个 对象的一个实例。
但我们随后必须指定自己想使用哪个接口,在由服务器实现的接口中挑选一个。这正是类型模型完成的工
作。这儿使用的是“默认遣送”接口,它是Automation 控制器用于同一个Automation 服务器通信的标准接
口。欲了解这方面的细节,请参考由Ibid 编著的《Inside 》。请注意激活服务器并选择一个 接口是
多么容易!
import javaadder。*;
public class JavaClient {
public static void main(String '' args) {
Adder_DispatchDefault iAdder =
(Adder_DispatchDefault) new Adder();
iAdder。setAddend(3);
iAdder。sum();
iAdder。sum();
iAdder。sum();
System。out。println(iAdder。getResult());
}
}
现在,我们可以编译它,并开始运行程序。
1。 。ms。 包
。ms。包为 的开发定义了数量众多的类。它支持GUID 的使用——Variant (变体)和SafeArray
665
…………………………………………………………Page 667……………………………………………………………
Automation (安全数组自动)类型——能与ActiveX 控件在一个较深的层次打交道,并可控制 异常。
由于篇幅有限,这里不可能涉及所有这些主题。但我想着重强调一下 异常的问题。根据规范,几乎所有
函数都会返回一个 HRESULT 值,它告诉我们函数调用是否成功,以及失败的原因。但若观察服务器和客
户代码中的 Java 方法签名,就会发现没有HRESULT 。相反,我们用函数返回值从一些函数那里取回数据。
“虚拟机”(VM )会将Java 风格的函数调用转换成 风格的函数调用,甚至包括返回参数。但假若我们在
服务器里调用的一个函数在 这一级失败,又会在虚拟机里出现什么事情呢?在这种情况下,JVM 会认为
HRESULT 值标志着一次失败,并会产生类。ms。。FailException 的一个固有 Java 异常。这样一来,
我们就可用 Java 异常控制机制来管理 错误,而不是检查函数的返回值。
如欲深入了解这个包内包含的类,请参考微软公司的产品文档。
A。5。5 Acti