在一次听课中第一次听说了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;
}
第二步:实现该接口。注意实现接口必须继承UnicastRemoteObject,实现代码如下:
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 + "!";
}
}
第三步:创建一个服务类,它的作用是创建RMI注册表,启动RMI服务,并将远程对象注册到RMI注册表中,实现代码如下:
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();
}
}
}
第四步:创建另一个java project工程,这个工程用来远程调用第一个工程的方法,这个工程只需包含2个类文件,IHello远程接口,代码同上。还需要一个类HelloClient类,它的作用是远程调用第一个工程的方法,实现代码如下:
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/