Java RMI 指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。所以,RMI相关对象必须运行在Java虚拟机中。
在Java中,只要一个类extends了java.rmi.Remote
接口,即可成为存在于服务器端的远程对象,供客户端访问并提供一定的服务。JavaDoc描述:Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”(扩展 java.rmi.Remote
的接口)中指定的这些方法才可远程使用。
在RMI中,当对象在虚拟机之间传递时,接收端虚拟机并不是复制一个远程对象,而是通过RMI接收一个远程对象的stub。这个stub就是对远程对象的引用,它其实是一个对远程对象的代理。接收端在本地调用了stub的方法,也就相当于调用了远程对象的方法。
接收端持有的远程对象的stub和远程对象实现的必须是同一组接口,这样才能将stub转换成远程对象接口类型,从而实现通过stub调用远程对象的方法。
1. 定义一个远程接口,此接口需要继承Remote,接口中的方法需要抛出一个`RemoteException`
2. 开发远程接口的实现类,这个类必须继承`UnicastRemoteObject`
3. 创建一个server并把远程对象注册到端口
4. 创建一个client查找远程对象,调用远程方法
下面我们就按照以上四步写一个分布式的Hello Word的例子来演示一下这个过程
编写RMI应用的第一步就是先定义远程接口。远程接口必须继承java.rmi.Remote
接口,并且声明自己的远程方法。为了处理远程方法发生的各种异常,每一个远程方法必须抛出一个java.rmi.RemoteException
异常。
public interface HelloService extends Remote{String sayHello(String name) throws RemoteException;
}
这个远程接口只定义了一个远程方法 sayHello()
,远程方法在调用的时候有可能失败比如发生网络问题或者server挂掉,此时远程方法会抛出RemoteException
异常。
开发接口的实现类,即具体的远程对象,在远程对象中实现远程接口中定义的方法。
public class HelloServiceImpl implements HelloService{public String sayHello(String name) {return "hello:"+name;}
}
在server端只需要做两件事:
1. 创建并导出远程对象
2. 用Java RMI registry 注册远程对象public class RMIServer {public static void main(String[] args) {try {HelloService helloService= new HelloServiceImpl();HelloService stub=(HelloService) UnicastRemoteObject.exportObject(helloService,0);Registry registry=LocateRegistry.createRegistry(1099);registry.bind( "helloword", stub);System. out.println( "绑定成功!");} catch (RemoteException e) {e.printStackTrace();} catch (AlreadyBoundException e) {e.printStackTrace();}}
}
HelloService helloService= new HelloServiceImpl();
HelloService stub=(HelloService) UnicastRemoteObject.exportObject(helloService,0);
Server端的main方法在创建一个远程对象来提供服务时,此远程对象必须被导出才能被远程调用者调用。静态方法UnicastRemoteObject.exportObject()
负责导出我们定义好的远程对象,它的第一个参数就是要到导出的远程对象,第二个参数是接收远程调用的tcp端口,这个值是0,它代表任意tcp端口。
exportObject()
还会返回一个导出的远程对象的存根,注意,前面讲过了这个stub的类型必须和远程对象的接口类型一致,因为这个存根是要发送给client端进行调用。同时这个方法还会抛出一个RemoteException
检查异常。
当exportObject()
方法被执行后,运行时会在一个新的Server Socket
或共享Server Socket
上进行监听,来接收对远程对象的远程调用。返回的存根对象和远程对象继承的是同一套remote接口,并且还它还包含了供client端口访问的主机IP和端口信息。
Registry registry=LocateRegistry.createRegistry(1099);registry.bind( "helloword", stub);
前面导出了远程对象的stub,它必须还能被client端找到并调用。为此,Java RMI 提供了 Registry API 可以允许应用程序把一个名称和远程对象的存根绑定在一起,这样client就可以通过这个绑定的名称很方便的查找到需要调用的远程对象了。
一旦远程对象在server端导出并注册,client就可以通过绑定的名称获得远程对象的引用,然后调用远程方法。
静态方法Registry registry=LocateRegistry.createRegistry(1099);
会返回一个实现了java.rmi.registry.Registry
接口的存根,并且在服务器本机的端口(默认是1099)上进行注册,返回的registry存根通过调用bind()
方法在registry中把一个字符串名称和远程对象存根绑定在一起。
public class RMIClient {public static void main(String[] args) {try {Registry registry = LocateRegistry.getRegistry();HelloService hello = (HelloService) registry.lookup( "helloword");String ret = hello.sayHello("heaven");System. out.println( ret);} catch (RemoteException e) {e.printStackTrace();} catch (NotBoundException e) {e.printStackTrace();}}
}
客户端首先通过LocateRegistry.getRegistry("localhost")
方法获得registry的存根,然后再执行registry存根的lookup()
方法从服务器registry中获得远程对象的存根,最后客户端在远程对象存根上执行sayHello()
方法。整个过程:
启动server,然后再启动client,控制台打印:
Hello: heaven!
Java RMI中用到了经典的工厂模式,先介绍下Java RMI应用的一些角色:
下面这幅图演示了整个步骤,下图中先做如下假设:
1. FactoryImpl被注册到了rmiregistry中
2. client端请求一个Factory的引用
3. rmiregistry返回client端一个FactoryImpl的引用
4. client端调用FactoryImpl的远程方法请求一个ProductImpl的远程引用
5. FactoryImpl返回给client端一个ProductImpl引用
6. client通过ProductImpl引用调用远程方法
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态