5 服务器接口
java.rmi.server
包含了通常用于实现远程对象的接口和类。
5.1 远程对象类
查看 RemoteObject
API 文档。
5.2 远程服务器类
查看 RemoteServer
API 文档。
5.3 单播远程对象类
java.rmi.server.UnicastRemoteObject
类提供了创建和导出远程对象的支持。该类实现了具有以下特征的远程服务器对象:
- 对这些对象的引用仅在创建远程对象的进程的生命周期内有效。
- 与远程对象的通信使用 TCP 传输。
- 调用、参数和结果使用流协议在客户端和服务器之间进行通信。
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
以利用其导出对象的构造函数,或者可以扩展其他类(或根本不扩展任何类)并通过 UnicastRemoteObject
的 exportObject
方法导出对象。
不带参数的构造函数在运行时在匿名(或任意)端口上创建并导出远程对象。第二种构造函数形式接受一个参数 port,指定远程对象接受传入调用的端口号。第三个构造函数创建并导出一个在指定 port 上接受传入调用的远程对象,通过从 RMIServerSocketFactory
创建的 ServerSocket
;客户端将通过从 RMIClientSocketFactory
提供的 Socket
连接到远程对象。
请注意,如果导出远程对象而不指定套接字工厂,或者使用不包含类型为 RMIClientSocketFactory
和 RMIServerSocketFactory
的参数的方法 UnicastRemoteObject.exportObject
或构造函数 UnicastRemoteObject
导出对象,则远程对象将导出到所有本地地址。要将远程对象导出到特定地址,请参阅 "RMI套接字工厂" 部分。
5.3.2 导出未从 RemoteObject
扩展的实现
使用 exportObject
方法(任何形式)导出一个简单的点对点远程对象,该对象未通过扩展 UnicastRemoteObject
类实现。第一种 exportObject
方法形式接受一个参数 obj,即将接受传入 RMI 调用的远程对象;此 exportObject
方法在运行时在匿名(或任意)端口上导出对象。第二个 exportObject
方法接受两个参数,即远程对象 obj 和端口号 port,远程对象接受传入调用的端口号。第三个 exportObject
方法在指定 port 上使用指定的 RMIClientSocketFactory
csf 和 RMIServerSocketFactory
ssf 导出对象 obj。
exportObject
方法返回一个 Remote
存根,该存根对象是传入 RMI 调用中传递的远程对象 obj
的存根对象。
5.3.3 在 RMI 调用中传递 UnicastRemoteObject
如上所述,当传递类型为 UnicastRemoteObject
的导出对象作为 RMI 调用的参数或返回值时,该对象将被远程对象的存根替换。导出的远程对象实现仍然保留在创建它的虚拟机中,并且不会移动(甚至按值)从该虚拟机中。换句话说,导出的远程对象在 RMI 调用中通过引用传递;导出的远程对象实现不能按值传递。
5.3.4 序列化 UnicastRemoteObject
包含在 UnicastRemoteObject
中的信息是瞬态的,如果将该类型的对象写入用户定义的 ObjectOutputStream
(例如,如果使用序列化将对象写入文件),则不会保存。然而,如果对象是 UnicastRemoteObject
的用户定义子类的实例,则可能具有可在对象序列化时保存的非瞬态数据。
当使用 UnicastRemoteObject
的 readObject
方法从 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.Socket
和java.net.ServerSocket
实例进行连接时,它不直接实例化这些类的对象,而是调用当前RMISocketFactory
对象上的createSocket
和createServerSocket
方法,这些对象是通过静态方法RMISocketFactory.getSocketFactory
返回的。这允许应用程序钩子自定义RMI传输使用的套接字类型,例如java.net.Socket
和java.net.ServerSocket
类的替代子类。要使用的RMISocketFactory
实例可以由受信任的系统代码设置一次。在JDK1.1中,此定制仅限于关于套接字类型的相对全局决策,因为工厂方法提供的唯一参数是host
和port
(对于createSocket
)以及仅port
(对于createServerSocket
)。
在Java SE平台中,引入了新的接口RMIServerSocketFactory
和RMIClientSocketFactory
,以提供更灵活的定制远程对象通信所使用的协议。
为了使使用RMI的应用程序能够利用这些新的套接字工厂接口,UnicastRemoteObject
类已添加了几个新的构造函数和exportObject
方法,这些方法接受客户端和服务器套接字工厂作为附加参数。
使用新构造函数或exportObject
方法(带有RMIClientSocketFactory
和RMIServerSocketFactory
参数)导出的远程对象将由RMI运行时以不同方式处理。对于此类远程对象的生命周期,运行时将使用自定义的RMIServerSocketFactory
创建ServerSocket
以接受对远程对象的传入调用,并使用自定义的RMIClientSocketFactory
创建Socket
以将客户端连接到远程对象。
用于导出具有旧构造函数或方法的远程对象UnicastRemoteObject
(不带自定义套接字工厂作为参数)将像以前一样具有UnicastRef
和UnicastServerRef
类型的RemoteRef
和ServerRef
,并使用其端点的旧线路表示形式,即以UTF格式的主机字符串后跟整数端口号。这样做是为了使不使用新1.2功能的RMI服务器能够与旧的RMI客户端进行交互。
如果导出远程对象而不指定套接字工厂,或者使用不包含RMIClientSocketFactory
和RMIServerSocketFactory
类型参数的UnicastRemoteObject.exportObject
方法或构造函数UnicastRemoteObject
的版本,则Java运行时将使用系统的默认RMI套接字工厂,该套接字工厂在通配符地址上打开套接字,监听所有接口。因此,远程对象将导出到所有本地地址。要将远程对象导出到特定地址,请执行以下操作之一:
-
使用方法
RMISocketFactory.setSocketFactory
指定套接字工厂。 -
实现接口
RMIClientSocketFactory
和RMIServerSocketFactory
,然后调用方法UnicastRemoteObject.exportObject(Remote obj, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
。或者,子类化类UnicastRemoteObject
并调用构造函数UnicastRemoteObject(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
。这种方法比调用方法RMISocketFactory.setSocketFactory
更灵活,因为它使您能够导出绑定到不同接口的不同对象。但是,这种方法更复杂。RMIClientSocketFactory
实现必须是可序列化的,因为实例通过嵌入在存根中传输到客户端。RMIClientSocketFactory
实现类必须对客户端可访问,通常通过配置客户端的RMI代码库指向类的可用位置。 -
不使用其中一个套接字工厂,设置接受仅来自特定Internet地址或域的传入连接的安全策略。此选项灵活,因为它允许您限制对特定网络、域或特定主机(包括仅
localhost
)的访问。请注意,使用此方法,Java运行时将使用打开在所有接口上监听的系统默认RMI套接字工厂。套接字仍然接受来自不允许的主机、域和网络的连接,但立即关闭这些连接而不处理任何RMI请求。
5.8.1 RMISocketFactory
类
java.rmi.server.RMISocketFactory
抽象类提供了一个接口,用于指定传输应如何获取套接字。请注意,下面的类使用java.net
包中的Socket
和ServerSocket
。
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.Socket
或java.net.ServerSocket
类的扩展,例如提供安全通信通道。请注意,只有在当前安全管理器允许设置套接字工厂时,才能设置RMISocketFactory
;如果禁止设置套接字工厂,将抛出SecurityException
。
静态方法getSocketFactory
返回RMI使用的套接字工厂。如果未设置套接字工厂,则该方法返回null
。
当传输需要创建套接字时,传输层会在由getSocketFactory
方法返回的RMISocketFactory
上调用createSocket
和createServerSocket
方法。例如:
RMISocketFactory.getSocketFactory().createSocket(myhost, myport)
方法createSocket
应创建连接到指定host和port的客户端套接字。方法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
的更多信息,请参阅以下网址:
- 对于 Linux 和 Mac 操作系统:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/rmic.html
- 对于 Windows 平台:
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/rmic.html