5 服务器接口


java.rmi.server 包含了通常用于实现远程对象的接口和类。

5.1 远程对象类

查看 RemoteObject API 文档。

5.2 远程服务器类

查看 RemoteServer API 文档。

5.3 单播远程对象类

java.rmi.server.UnicastRemoteObject 类提供了创建和导出远程对象的支持。该类实现了具有以下特征的远程服务器对象:

package java.rmi.server;

public class UnicastRemoteObject extends RemoteServer {

        protected UnicastRemoteObject()
                throws java.rmi.RemoteException {...}
        protected UnicastRemoteObject(int port)
                throws java.rmi.RemoteException {...}
        protected UnicastRemoteObject(int port,
                                      RMIClientSocketFactory csf,
                                      RMIServerSocketFactory ssf)
                throws java.rmi.RemoteException {...}

        public Object clone()
                throws java.lang.CloneNotSupportedException {...}
        public static RemoteStub exportObject(java.rmi.Remote obj)
                throws java.rmi.RemoteException {...}
        public static Remote exportObject(java.rmi.Remote obj, int port)
                throws java.rmi.RemoteException {...}
        public static Remote exportObject(Remote obj, int port,
                                          RMIClientSocketFactory csf,
                                          RMIServerSocketFactory ssf)
                throws java.rmi.RemoteException {...}
        public static boolean unexportObject(java.rmi.Remote obj,
                                             boolean force)
                throws java.rmi.NoSuchObjectException {...}
}

5.3.1 构造新的远程对象

远程对象实现(实现一个或多个远程接口的对象)必须被创建和导出。导出远程对象使该对象能够接受来自客户端的调用。对于作为 UnicastRemoteObject 导出的远程对象实现,导出涉及侦听 TCP 端口(请注意,多个远程对象可以在同一端口上接受传入调用,因此并非总是需要侦听新端口)。远程对象实现可以扩展类 UnicastRemoteObject 以利用其导出对象的构造函数,或者可以扩展其他类(或根本不扩展任何类)并通过 UnicastRemoteObjectexportObject 方法导出对象。

不带参数的构造函数在运行时在匿名(或任意)端口上创建并导出远程对象。第二种构造函数形式接受一个参数 port,指定远程对象接受传入调用的端口号。第三个构造函数创建并导出一个在指定 port 上接受传入调用的远程对象,通过从 RMIServerSocketFactory 创建的 ServerSocket;客户端将通过从 RMIClientSocketFactory 提供的 Socket 连接到远程对象。

请注意,如果导出远程对象而不指定套接字工厂,或者使用不包含类型为 RMIClientSocketFactoryRMIServerSocketFactory 的参数的方法 UnicastRemoteObject.exportObject 或构造函数 UnicastRemoteObject 导出对象,则远程对象将导出到所有本地地址。要将远程对象导出到特定地址,请参阅 "RMI套接字工厂" 部分。

5.3.2 导出未从 RemoteObject 扩展的实现

使用 exportObject 方法(任何形式)导出一个简单的点对点远程对象,该对象未通过扩展 UnicastRemoteObject 类实现。第一种 exportObject 方法形式接受一个参数 obj,即将接受传入 RMI 调用的远程对象;此 exportObject 方法在运行时在匿名(或任意)端口上导出对象。第二个 exportObject 方法接受两个参数,即远程对象 obj 和端口号 port,远程对象接受传入调用的端口号。第三个 exportObject 方法在指定 port 上使用指定的 RMIClientSocketFactory csfRMIServerSocketFactory ssf 导出对象 obj

exportObject 方法返回一个 Remote 存根,该存根对象是传入 RMI 调用中传递的远程对象 obj 的存根对象。

5.3.3 在 RMI 调用中传递 UnicastRemoteObject

如上所述,当传递类型为 UnicastRemoteObject 的导出对象作为 RMI 调用的参数或返回值时,该对象将被远程对象的存根替换。导出的远程对象实现仍然保留在创建它的虚拟机中,并且不会移动(甚至按值)从该虚拟机中。换句话说,导出的远程对象在 RMI 调用中通过引用传递;导出的远程对象实现不能按值传递。

5.3.4 序列化 UnicastRemoteObject

包含在 UnicastRemoteObject 中的信息是瞬态的,如果将该类型的对象写入用户定义的 ObjectOutputStream(例如,如果使用序列化将对象写入文件),则不会保存。然而,如果对象是 UnicastRemoteObject 的用户定义子类的实例,则可能具有可在对象序列化时保存的非瞬态数据。

当使用 UnicastRemoteObjectreadObject 方法从 ObjectInputStream 读取 UnicastRemoteObject 时,远程对象将自动导出到 RMI 运行时,以便它可以接收 RMI 调用。如果由于某种原因导出对象失败,则反序列化对象将以异常终止。

5.3.5 取消导出 UnicastRemoteObject

unexportObject 方法使远程对象 obj 不再可用于传入调用。如果 force 参数为 true,则即使有待处理的调用或远程对象仍在进行调用,也会强制取消导出对象。如果 force 参数为 false,则仅当没有待处理或正在进行的调用对象时才会取消导出对象。如果成功取消导出对象,则 RMI 运行时会从其内部表中删除对象。以这种强制方式取消导出对象可能会使客户端持有过时的远程对象引用。如果对象之前未导出到 RMI 运行时,则此方法将抛出 java.rmi.NoSuchObjectException

5.3.6 clone 方法

只有支持 java.lang.Cloneable 接口的对象才能使用 Java 编程语言的默认机制进行克隆。类 java.rmi.server.UnicastRemoteObject 没有实现此接口,但实现了 clone 方法,因此如果子类需要实现 Cloneable,则远程对象将能够正确克隆。 clone 方法可由子类使用,以创建一个具有最初相同内容的克隆远程对象,但被导出以接受远程调用,并且与原始对象不同。

5.4 未引用接口

package java.rmi.server;

public interface Unreferenced {
        public void unreferenced();
}

java.rmi.server.Unreferenced 接口允许服务器对象接收通知,表示没有客户端持有对其的远程引用。分布式垃圾回收机制为每个远程对象维护了持有对该远程对象引用的客户端虚拟机集合。只要某个客户端持有对远程对象的远程引用,RMI 运行时就会保留对远程对象的本地引用。每当远程对象的“引用”集合变为空(表示引用该对象的客户端数量变为零)时,如果该远程对象实现了 Unreferenced 接口,则将调用 Unreferenced.unreferenced 方法。远程对象 需要支持 Unreferenced 接口。

只要存在对远程对象的某个本地引用,它可能会在远程调用中传递或返回给客户端。接收引用的进程将添加到远程对象的引用集合中。当引用集合变为空时,将调用远程对象的 unreferenced 方法。因此,unreferenced 方法可能会被调用多次(每次集合被新清空时)。只有当不再存在引用,无论是本地引用还是客户端持有的引用时,远程对象才会被收集。

5.5 RMISecurityManager

查看 RMISecurityManager API 文档。

5.6 RMIClassLoader

查看 RMIClassLoader API 文档。

5.7 LoaderHandler 接口

查看 LoaderHandler API 文档。

5.8 RMI套接字工厂

当RMI运行时实现需要java.net.Socketjava.net.ServerSocket实例进行连接时,它不直接实例化这些类的对象,而是调用当前RMISocketFactory对象上的createSocketcreateServerSocket方法,这些对象是通过静态方法RMISocketFactory.getSocketFactory返回的。这允许应用程序钩子自定义RMI传输使用的套接字类型,例如java.net.Socketjava.net.ServerSocket类的替代子类。要使用的RMISocketFactory实例可以由受信任的系统代码设置一次。在JDK1.1中,此定制仅限于关于套接字类型的相对全局决策,因为工厂方法提供的唯一参数是hostport(对于createSocket)以及仅port(对于createServerSocket)。

在Java SE平台中,引入了新的接口RMIServerSocketFactoryRMIClientSocketFactory,以提供更灵活的定制远程对象通信所使用的协议。

为了使使用RMI的应用程序能够利用这些新的套接字工厂接口,UnicastRemoteObject类已添加了几个新的构造函数和exportObject方法,这些方法接受客户端和服务器套接字工厂作为附加参数。

使用新构造函数或exportObject方法(带有RMIClientSocketFactoryRMIServerSocketFactory参数)导出的远程对象将由RMI运行时以不同方式处理。对于此类远程对象的生命周期,运行时将使用自定义的RMIServerSocketFactory创建ServerSocket以接受对远程对象的传入调用,并使用自定义的RMIClientSocketFactory创建Socket以将客户端连接到远程对象。

用于导出具有旧构造函数或方法的远程对象UnicastRemoteObject(不带自定义套接字工厂作为参数)将像以前一样具有UnicastRefUnicastServerRef类型的RemoteRefServerRef,并使用其端点的旧线路表示形式,即以UTF格式的主机字符串后跟整数端口号。这样做是为了使不使用新1.2功能的RMI服务器能够与旧的RMI客户端进行交互。

如果导出远程对象而不指定套接字工厂,或者使用不包含RMIClientSocketFactoryRMIServerSocketFactory类型参数的UnicastRemoteObject.exportObject方法或构造函数UnicastRemoteObject的版本,则Java运行时将使用系统的默认RMI套接字工厂,该套接字工厂在通配符地址上打开套接字,监听所有接口。因此,远程对象将导出到所有本地地址。要将远程对象导出到特定地址,请执行以下操作之一:

5.8.1 RMISocketFactory

java.rmi.server.RMISocketFactory抽象类提供了一个接口,用于指定传输应如何获取套接字。请注意,下面的类使用java.net包中的SocketServerSocket

package java.rmi.server;

public abstract class RMISocketFactory
        implements RMIClientSocketFactory, RMIServerSocketFactory
{

        public abstract Socket createSocket(String host, int port)
                throws IOException;
   public abstract ServerSocket createServerSocket(int port)
                throws IOException;
        public static void setSocketFactory(RMISocketFactory fac)
                throws IOException {...}
        public static RMISocketFactory getSocketFactory() {...}
        public static void setFailureHandler(RMIFailureHandler fh) {...}
        public static RMIFailureHandler getFailureHandler() {...}
}

静态方法setSocketFactory用于设置RMI获取套接字的套接字工厂。应用程序可能仅调用此方法一次,并使用自己的RMISocketFactory实例。例如,RMISocketFactory的应用程序定义实现可以对请求的连接进行初步过滤并抛出异常,或返回其自己的java.net.Socketjava.net.ServerSocket类的扩展,例如提供安全通信通道。请注意,只有在当前安全管理器允许设置套接字工厂时,才能设置RMISocketFactory;如果禁止设置套接字工厂,将抛出SecurityException

静态方法getSocketFactory返回RMI使用的套接字工厂。如果未设置套接字工厂,则该方法返回null

当传输需要创建套接字时,传输层会在由getSocketFactory方法返回的RMISocketFactory上调用createSocketcreateServerSocket方法。例如:

RMISocketFactory.getSocketFactory().createSocket(myhost, myport)

方法createSocket应创建连接到指定hostport的客户端套接字。方法createServerSocket应在指定port上创建服务器套接字。

方法setFailureHandler设置在创建服务器套接字失败时RMI运行时应调用的失败处理程序。失败处理程序返回一个布尔值,指示是否应重试。默认的失败处理程序返回false,这意味着默认情况下运行时不会尝试重新创建套接字。

方法getFailureHandler返回当前的套接字创建失败处理程序,如果未设置失败处理程序,则返回null

5.8.2 RMIServerSocketFactory接口

请参阅RMIServerSocketFactory API文档。

5.8.3 RMIClientSocketFactory接口

请参阅RMIClientSocketFactory API文档。

5.9 RMIFailureHandler接口

java.rmi.server.RMIFailureHandler接口提供了一种指定RMI运行时在服务器套接字创建失败时(在对象导出期间除外)应如何响应的方法。

package java.rmi.server;

public interface RMIFailureHandler {

        public boolean failure(Exception ex);
}

failure方法使用阻止RMI运行时创建java.net.ServerSocket的异常调用。如果运行时应尝试重试,则该方法返回true,否则返回false

在调用此方法之前,需要通过RMISocketFactory.setFailureHandler调用注册失败处理程序。如果未设置失败处理程序,则RMI运行时会在等待一段时间后尝试重新创建ServerSocket

请注意,当初始导出对象时ServerSocket创建失败时,不会调用RMIFailureHandler。只有在对该ServerSocket进行失败接受后尝试创建ServerSocket时才会调用RMIFailureHandler

5.10 LogStream

请参阅LogStream API文档。

5.11 存根和骨架编译器

rmic 存根和骨架编译器用于为特定远程对象实现编译适当的存根和骨架。

有关 rmic 的更多信息,请参阅以下网址:

    https://docs.oracle.com/javase/8/docs/technotes/tools/unix/rmic.html
    https://docs.oracle.com/javase/8/docs/technotes/tools/windows/rmic.html