在一次听课中第一次听说了RMI协议,对于我们这些没有接触过EJB的程序员来说感觉RMI是一个挺新奇的东西,居然能够让某个java虚拟机上的对象调用另一个java虚拟机中的对象上的方法,感觉挺有用的,就回来研究了下RMI的原理和用法。
百度上这么解释RMI:RMI是Java的一组拥护开发分布式应用程序的API。RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用,由于J2EE是分布式程序平台,它以RMI机制实现程序组件在不同操作系统之间的通信。
rmi的详细介绍可以参开 http://docs.huihoo.com/java/rmi/whitepage/index.html
下面根据看到的一个简单例子来让我们简单的使用rmi。
第一步:首先创建一个java project工程,在工程中先定义一个远程接口IHello,注意该远程接口必须继承Remote接口,其中需要远程调用的方法必须抛出RemoteException异常,且远程接口必须为public。代码如下:
package com.rmi.demo; import java.rmi.Remote; import java.rmi.RemoteException; /** * 定义一个远程接口,必须继承Remote接口,其中需要远程调用的方法必须抛出RemoteException异常 */ public interface IHello extends Remote { /** * @return 简单的返回"hello world"字符串 * @throws RemoteException */ public String helloWorld() throws RemoteException; /** * 一个简单的业务方法,根据传入的人名返回相应的问候语 * @param someBodyName 人名 * @return 返回相应的问候语 * @throws java.rmi.RemoteException */ public String sayHelloToSomeBody(String someBodyName) throws RemoteException; }
package com.rmi.demo; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * 远程的接口的实现 */ public class HelloImpl extends UnicastRemoteObject implements IHello { /** * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常, * 因此这里默认的构造方法必须写,必须声明抛出RemoteException异常 * * @throws RemoteException */ public HelloImpl() throws RemoteException { } /** * @return 返回"hello world"字符串 * @throws java.rmi.RemoteException */ public String helloWorld() throws RemoteException { return "hello world!"; } /** * 一个简单的业务方法,根据传入的人名返回相应的问候语 * * @param someBodyName 人名 * @return 返回相应的问候语 * @throws java.rmi.RemoteException */ public String sayHelloToSomeBody(String someBodyName) throws RemoteException { return "你好," + someBodyName + "!"; } }
package com.rmi.demo; import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; /** * 创建RMI注册表,启动RMI服务,并将远程对象注册到RMI注册表中。 */ public class HelloServer { public static void main(String args[]) { try { //创建一个远程对象 IHello rhello = new HelloImpl(); //本地主机上的远程对象注册表Registry的实例,并指定端口为8888, //这一步必不可少(Java默认端口是1099),必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上 LocateRegistry.createRegistry(8888); //把远程对象注册到RMI注册服务器上,并命名为RHello //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的) Naming.bind("rmi://localhost:8888/RHello",rhello); //Naming.bind("//localhost:8888/RHello",rhello); System.out.println(">>>>>INFO:远程IHello对象绑定成功!"); } catch (RemoteException e) { System.out.println("创建远程对象发生异常!"); e.printStackTrace(); } catch (AlreadyBoundException e) { System.out.println("发生重复绑定对象异常!"); e.printStackTrace(); } catch (MalformedURLException e) { System.out.println("发生URL畸形异常!"); e.printStackTrace(); } } }
package com.rmi.demo; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * 客户端测试,在客户端调用远程对象上的远程方法,并返回结果。 */ public class HelloClient { public static void main(String args[]){ try { //在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法 IHello rhello =(IHello) Naming.lookup("rmi://localhost:8888/RHello"); System.out.println(rhello.helloWorld()); System.out.println(rhello.sayHelloToSomeBody("rmi")); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }
先运行HelloServer的main方法,如果输出【>>>>>INFO:远程IHello对象绑定成功!】,则说明已经成功启动RMI服务
然后运行HelloClient的main方法,然后会输出:
hello world!
你好,rmi!
则说明测试成功,一个应用成功的调用了另外一个应用的方法,并得到返回值了。
ps:在调试过程中碰到一个异常,如果二个应用的远程接口的package的路径不一样的时候会抛出如下异常:java.lang.ClassNotFoundException: com.rmi.demo.IHello (no security manager: RMI class loader disabled),这是因为在将远程对象注册到RMI注册表中时,会附带上package信息,如果服务端与客户端的远程接口所放得包不一样,就会找不到对应的实现接口,抛出异常。
参考博客:
http://lavasoft.blog.51cto.com/62575/91679/
http://docs.huihoo.com/java/rmi/whitepage/index.html
http://www.233.com/Java/jichu/20070214/151627705.html