快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

漫谈EJB (2)

EJB技术的基础是另外两种技术:RMI-IIOP和JNDI。要想了解EJB,一定要先了解RMI-IIOP和JNDI。因此,我们在介绍EJB细节之前,先了解这两项技术。我们的介绍比较基本,因此大多数组织只要了解这些就已经够了。

Java RMI-IIOP

Java RMI-IIOP(Java Remote Method Invocation over the Internet Inter-ORB Protocol)是J2EE的网络机制。Java RMI-IIOP答应你编写分布式对象,使得对象的通信范围能够在内存中,跨Java虚拟机,跨物理设备。

Remote Method Invocation

RPC(remote procedure call)是一台机器的进程调用另一台机器的进程的过程。而remote method invocation则比RPC的概念更进一步,答应分布式对象间的通信。RMI-IIOP答应调用远程对象的方式,而不仅仅是过程。这有利于面向对象编程。Java RMI (Remote Method Invocation 远程方式调用)是用Java在JDK1.1中实现的,它大大增强了Java开发分布式应用的能力。Java作为一种风靡一时的网络开发语言,其巨大的威力就体现在它强盛的开发分布式网络应用的能力上,而RMI就是开发百分之百纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。

remote method invocation决不简朴,需要考虑几个问题:

marshalling和unmarshalling.在不同机器间通过网络传递变量(包括Java基本类型和对象),假如目标机器表示数据的方法和原机器不同该怎么办?例如二进制库不同。因此marshalling和unmarshalling就是传递变量的过程。

变量传递方式.变量有两种传递方式:pass-by-value和pass-by-reference。对于前者,你的目标方式只需使用一份copy,但对于后者,远程方式对变量的任何修改都会影响到源数据。

网络和机器的不稳定.需要有一种机制保证一个JVM崩溃之后,不会影响系统的正常运作。

在 Java 分布式对象模型中,remote object 是这样一种对象:它的方式可以从其它 Java 虚拟机(可能在不同的主机上)中调用。该类型的对象由一种或多种 remote interfaces(它是声明远程对象方式的 Java 接口)描述。远程方式调用 (RMI) 就是调用远程对象上远程接口的方式的动作。更为重要的是,远程对象的方式调用与本地对象的方式调用语法一样。

Remote Interface

RMI-IIOP遵循了接口和实现的原则。你写的所有网络代码都是应用于接口,而不是实现。实际上,你必须使用RMI-IIOP中的范例,没有其它的选择。直接在你的对象实现上执行远程调用是不可能的,你只能在对象类的接口上单独进行这一操作。

所以我们在使用RMI-IIOP时,你必须建立一个客户接口,叫做remote interface。这个远程接口应该扩展java.rmi.Remote接口。

Remote Object Implementation

远程对象和客户机的物理位置并不是很重要。可以运行在同一地址空间或是跨Internet运行。

为了使对象成为一个远程对象,你需要执行一下步骤:

继续javax.rmi.PortableRemoteObject。PortableRemoteObject是进行远程调用的基类,当你的远程对象调用构造器时,PortableRemoteObject对象的构造器也会自动被调用。

不继续javax.rmi.PortableRemoteObject。假如你的远程对象需要继续其它的类,而Java不答应多重继续,因此你不能继续PortableRemoteObject。这时,你需要手动调用javax.rmi.PortableRemoteObject.exportObject()。

Stub和Skeletons

我们来看看在RMI-IIOP背后隐藏的网络架构。RMI-IIOP的一个好处就是你可以不用管你要调用的对象是本地的还是远程的。这就叫做local/remote transparency。

RMI应用程序通常包括两个独立的程序:服务器程序和客户机程序。典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调用这些远程对象的方式。而典型的客户机程序则从服务器中得到一个或多个远程对象的引用,然后调用远程对象的方式。RMI为服务器和客户机进行通信和信息传递提供了一种机制。






在与远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色。调用程序将调用本地stub的方式,而本地stub将负责执行对远程对象的方式调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集一样。调用stub的方式时将执行下列操作:(1) 初始化与包含远程对象的远程虚拟机的连接;(2) 对远程虚拟机的参数进行编组(写入并传输);(3) 等待方式调用结果;(4) 解编(读取)返回值或返回的非常;(5) 将值返回给调用程序。为了向调用程序展示比较简朴的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton(在JDK1.2环境中无需使用skeleton)。Skeleton负责将调用分配给实际的远程对象实现。它在接收方式调用时执行下列操作:(1) 解编(读取)远程方式的参数;(2) 调用实际远程对象实现上的方式;(3) 将结果(返回值或非常)编组(写入并传输)给调用程序。stub和skeleton由rmic编译器生成。

要实现local/remote transparency可没有那么简朴。为了屏蔽你调用的是远端主机上的对象,RMI-IIOP需要模仿一个本地对象供你调用。这个本地对象叫做stub。它负责接受本地的方式调用哀求,把这些哀求委托给真正实现它们的对象(可以通过网络定位)。这样就使得远程调用看起来就和本地调用相同。

利用RMI编写分布式对象应用程序需要完成以下工作:(1) 定位远程对象。应用程序可使用两种机制中的一种得到对远程对象的引用。它既可用RMI的简朴命名工具rmiregistry来注册它的远程对象,也可以将远程对象引用作为常规操作的一部分来进行传递和返回。(2)与远程对象通信。远程对象间通信的细节由RMI处理,对于程序员来说,远程通信看起来就像标准的Java方式调用。(3)给作为参数或返回值传递的对象加载类字节码。因为RMI答应调用程序将纯Java对象传给远程对象,所以,RMI将提供必要的机制,既可以加载对象的代码又可以传输对象的数据。在RMI分布式应用程序运行时,服务器调用注册服务程序以使名字与远程对象相关联。客户机在服务器上的注册服务程序中用远程对象的名字查找该远程对象,然后调用它的方式。

定位远程对象。应用程序可使用两种机制中的一种得到对远程对象的引用。它既可用 RMI 的简朴命名工具 rmiregistry 来注册它的远程对象;也可将远程对象引用作为常规操作的一部分来进行传递和返回。

与远程对象通讯。远程对象间通讯的细节由 RMI 处理;对于程序员来说,远程通讯看起来就象标准的 Java 方式调用。给作为参数或返回值传递的对象加载类字节码因为 RMI答应调用程序将纯 Java 对象传给远程对象,所以 RMI 将提供必要的机制,既可以加载对象的代码又可以传输对象的数据。服务器调用注册服务程序以使名字与远程对象相关联。客户机在服务器注册服务程序中用远程对象的名字查找该远程对象,然后调用它的方式。RMI 能用 Java系统支持的任何 URL 协议(例如 HTTP、FTP、file 等)加载类字节码。

stub只是解决了一半的问题。我们还希望远程对象也不用考虑网络问题。因此远程对象也需要一个本地的skeleton来接受调用。skeleton接受网络调用并把调用委托给远程对象实现。

你的J2EE服务器应当提供一种方式来产生必须的stub和skeleton,以减轻你的对网络问题考虑的负担。典型的是通过命令行工具来完成,例如sun的J2EE参考实现包就使用了一个名为rmic(RMI compiler)的工具来产生stub和skeleton类。你应当把stub部署在客户机上,并把skeleton部署在服务器上。

对象序列化和变量传递

在RMI分布式应用系统中,服务器与客户机之间传递的Java对象必须是可序列化的对象。不可序列化的对象不能在对象流中进行传递。对象序列化扩展了核心Java输入/输出类,同时也支持对象。对象序列化支持把对象编码以及将通过它们可访问到的对象编码变成字节流;同时,它也支持流中对象图形的互补重构造。序列化用于轻型持久性和借助于套接字或远程方式调用(RMI)进行的通信。序列化中现在包括一个 API(Application Programming Interface,应用程序接口),答应独立于类的域指定对象的序列化数据,并答应使用现有协议将序列化数据域写入流中或从流中读取,以确保与缺省读写机制的兼容性。

为编写应用程序,除多数瞬态应用程序外,都必须具备存储和检索 Java对象的能力。以序列化方法存储和检索对象的要害在于提供重新构造该对象所需的足够对象状态。存储到流的对象可能会支持 Serializable(可序列化)或 Externalizable(可外部化)接口。对于Java对象,序列化形式必须能标识和校验存储其内容的对象所属的 Java类,并且将该内容还原为新的实例。对于可序列化对象,流将提供足够的信息将流的域还原为类的兼容版本。对于可外部化对象,类将全权负责其内容的外部格式。序列化 Java 对象的目的是:提供一种简朴但可扩充的机制,以序列化方法维护 Java对象的类型及安全属性;具有支持编组和解编的扩展能力以满意远程对象的需要;具有可扩展性以支持 Java 对象的简朴持久性;只有在自定义时,才需对每个类提供序列化自实现;答应对象定义其外部格式。

java.rmi.Remote 接口

在 RMI 中,远程接口是声明了可从远程 Java 虚拟机中调用的方式集。远程接
口必须满意下列要求:

远程接口至少必须直接或间接扩展 java.rmi.Remote 接口。
远程接口中的方式声明必须满意下列远程方式声明的要求:
远程方式声明在其 throws 子句中除了要包含与应用程序有关的非常(注重与应用程序有关的非常无需扩展 java.rmi.RemoteException )之外,还必须包括 java.rmi.RemoteException 非常(或它的超类,例如java.io.IOException 或 java.lang.Exception )。
远程方式声明中,作为参数或返回值声明的(在参数表中直接声明或嵌入到参数的非远程对象中)远程对象必须声明为远程接口,而非该接口的实现类。

java.rmi.Remote 接口是一个不定义方式的标记接口:

public interface Remote

远程接口必须至少扩展 java.rmi.Remote 接口(或其它扩展java.rmi.Remote 的远程接口)。然而,远程接口在下列情况中可以扩展非远程接口:

远程接口也可扩展其它非远程接口,只要被扩展接口的所有方式(假如有)满意远程方式声明的要求。
例如,下面的接口 BankAccount 即为访问银行帐户定义了一个远程接口。它包含往帐户存款、使帐户收支平衡和从帐户取款的远程方式:

public interface BankAccount extends java.rmi.Remote
{
public void deposit(float amount)
throws java.rmi.RemoteException;
public void withdraw(float amount)
throws OverdrawnException, java.rmi.RemoteException;
public float getBalance()
throws java.rmi.RemoteException;
}

下例说明了有效的远程接口 Beta。它扩展非远程接口 Alpha(有远程方式)和接口 java.rmi.Remote:
public interface Alpha
{
public final String okay = "constants are okay too";
public Object foo(Object obj)
throws java.rmi.RemoteException;
public void bar() throws java.io.IOException;
public int baz() throws java.lang.Exception;
}

public interface Beta extends Alpha, java.rmi.Remote {
public void ping() throws java.rmi.RemoteException;
}

RemoteException 类

java.rmi.RemoteException 类是在远程方式调用期间由 RMI 运行时所抛出的非常的超类。为确保使用 RMI 系统的应用程序的健壮性,远程接口中声明的远程方式在其 throws 子句中必须指定 java.rmi.RemoteException(或它的超类,例如 java.io.IOException 或 java.lang.Exception)。

当远程方式调用由于某种原因失败时,将抛出 java.rmi.RemoteException 非常。远程方式调用失败的原因包括:

通讯失败(远程服务器不可达或拒绝连接;连接被服务器关闭等。)
参数或返回值传输或读取时失败
协议错误

RemoteException 类是一个已检验的非常(必须由远程方式的调用程序处理并经编译器检验的非常),而不是 RuntimeException。

RemoteObject 类及其子类

RMI 服务器函数由 java.rmi.server.RemoteObject 及其子类java.rmi.server.RemoteServer、java.rmi.server.UnicastRemoteObject和 java.rmi.activation.Activatable 提供。

java.rmi.server.RemoteObject 为对远程对象敏感的 java.lang.Object方式、hashCode、 equals 和 toString 提供实现。

创建远程对象并将其导出(使它们可为远程客户机利用)所需的方式由类UnicastRemoteObject 和 Activatable 提供。子类可以识别远程引用的语义,例如服务器是简朴的远程对象还是可激活的远程对象(调用时将执行的远程对象)。java.rmi.server.UnicastRemoteObject 类定义了单体(单路传送)远程对
象,其引用只有在服务器进程活着时才有效。类 java.rmi.activation.Activatable 是抽象类,它定义的 activatable远程对象在其远程方式被调用时开始执行并在必要时自己关闭。

实现远程接口

实现远程接口的类的一般规则如下:

该类通常扩展 java.rmi.server.UnicastRemoteObject,因而将继续类java.rmi.server.RemoteObject 和java.rmi.server.RemoteServer 提供的远程行为。
该类能实现任意多的远程接口。
该类能扩展其它远程实现类。
该类能定义远程接口中不出现的方式,但这些方式只能在本地使用而不能在远程使用。

例如,下面的类 BankAcctImpl 实现 BankAccount 远程接口并扩展java.rmi.server.UnicastRemoteObject 类:

package mypackage;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class BankAccountImpl extends UnicastRemoteObject implements
BankAccount

{
private float balance = 0.0;

public BankAccountImpl(float initialBalance)
throws RemoteException
{
balance = initialBalance;
}

public void deposit(float amount) throws RemoteException
{
...
}

public void withdraw(float amount) throws OverdrawnException,
RemoteException
{
...
}

public float getBalance() throws RemoteException
{
...
}
}


注重:必要时,实现远程接口的类能扩展除java.rmi.server.UnicastRemoteObject 类以外的其它一些类。但实现类此时必须承担起一定的责任,即导出对象(由 UnicastRemoteObject 构造函数负责)和实现从 java.lang.Object 类继续的 hashCode、 equals 和toString 方式的准确远程语义(假如需要)。


远程方式调用中的参数传递

传给远程对象的参数或源于它的返回值可以是任意可序列化的 Java 对象。这包括 Java 基本类型, 远程?Java 对象和实现 java.io.Serializable 接口的非远程 Java 对象。有关如何使类序列化的具体信息,参见 Java“对象序列化规范”。本地得不到的作为参数或返回值的类,可通过 RMI 系统进行动态下载。

传递非远程对象

非远程对象将作为远程方式调用的参数传递或作为远程方式调用的结果返回时,是通过复制传递的;也就是使用 Java 对象序列化机制将该对象序列化。因此,在远程对象调用过程中,当非远程对象作为参数或返回值传递时,非远程对象的内容在调用远程对象之前将被复制。从远程方式调用返回非远程对象时,将在调用的虚拟机中创建新对象。

传递远程对象

当将远程对象作为远程方式调用的参数或返回值传递时,远程对象的 stub 程序即被传递出去。作为参数传递的远程对象仅能实现远程接口。

引用的完整性

假如一个对象的两个引用在单个远程方式调用中以参数形式(或返回值形式)从一个虚拟机传到另一个虚拟机中,并且它们在发送虚拟机中指向同一对象,则两个引用在接收虚拟机中将指向该对象的同一副本。进一步说就是:在单个远程方式调用中,RMI 系统将在作为调用参数或返回值传递的对象中保持引用的完整性。

类注解

当对象在远程调用中被从一个虚拟机发送到另一个虚拟机中时,RMI 系统在调用流中用类的信息 (URL) 给类描述符加注解,以便该类能在接收器上加载。在远程方式调用期间,调用可随时下载类。

参数传输

为将 RMI 调用的参数序列化到远程调用的目的文件里,需要将该参数写入作为java.io.ObjectOutputStream 类的子类的流中。ObjectOutputStream 子类将覆盖 replaceObject 方式,目的是用其相应的 stub 类取代每个远程对象。对象参数将通过 ObjectOutputStream 的 writeObject 方式写入流中。而ObjectOutputStream 则通过 writeObject 方式为每个写入流中的对象(包含所写对象所引用的对象)调用 replaceObject 方式。RMIObjectOutputStream子类的 replaceObject 方式返回下列值:
假如传给 replaceObject 的对象是 java.rmi.Remote 的实例,则返回远程对象的 stub 程序。远程对象的 stub 程序通过对java.rmi.server.RemoteObject.toStub方式的调用而获得。假如传给 replaceObject 的对象不是 java.rmi.Remote 的实例,则只返回该对象。

RMI 的 ObjectOutputStream 子类也实现 annotateClass 方式,该方式用类的位置注解调用流以便能在接收器中下载该类。有关如何使用 annotateClass的具体信息,参见“动态类加载”一节。因为参数只写入一个 ObjectOutputStream,所以指向调用程序同一对象的引用将在接收器那里指向该对象的同一副本。在接收器上,参数将被单个ObjectInputStream 所读取。

用于写对象的 ObjectOutputStream(类似的还有用于读对象的ObjectInputStream )的所有其它缺省行为将保留在参数传递中。例如,写对象时对 writeReplace 的调用及读对象时对 readResolve 的调用就是由 RMI的参数编组与解编流完成的。

与上述 RMI 参数传递方法类似,返回值(或非常)将被写入ObjectOutputStream的子类并和参数传输的替代行为一样。

定位远程对象

我们专门提供了一种简朴的引导名字服务器,用于存储对远程对象的已命名引用。使用类 java.rmi.Naming 的基于 URL 的方式可以存储远程对象引用。客户机要调用远程对象的方式,则必须首先得到该对象的引用。对远程对象的引用通常是在方式调用中以返回值的形式取得。RMI 系统提供一种简朴的引导名字服务器,通过它得到给定主机上的远程对象。java.rmi.Naming 类提供基于统一资源定位符 (URL) 的方式,用来绑定、再绑定、解开和列出位于某一主机及端口上的名字-对象对。
返回类别: 教程
上一教程: 在JSP程序如何解决读取或向DB写入数据时的乱码问题
下一教程: SERVLET 和 JSP 性能调整

您可以阅读与"漫谈EJB (2)"相关的教程:
· 漫谈EJB (1)
· 漫谈EJB (3)
· 漫谈Java语言的接口与类型安全
· 漫谈Java程序设计中的接口应用
· Jboss EJB 3.0--Stateless Beans
    微笑服务 优质保证 索取样品