RMI是Remote Method Invocation(远程方法调用)的 所写。它允许一个Java程序调用网络中另一台计算机上的Java方法,就如调用本机的方法一样。实现RMI调用的程序和被调用的方法,都必须是Java代码,即客户端和服务器端都必须通过纯Java实现。
RMI是基于Java的分布式编程模型,使用RMI进行远程方法调用时,无须考虑方法底层的网络传输细节。下面使用RMI的示例程序:
先编写RMI服务器端,RMI需要通过远程接口“暴露”服务。也就是说,所有想被客户机调用的方法都必须在Remote接口里声明,否则无法完成调用。远程接口如下:
远程接口必须集成java.rmi.Remote接口
public interface Server extends Remote
{
//所有在Remote接口里声明的方法都必须抛出RemoteException异常
String helloWorld(String name) throws RemoteException;
Person getPerson(String name,int age)throws RemoteException;
}
远程接口必须继承java.rmi.Remote接口。远程接口里声明的方法会通过网络传输,而网络是不可靠的,因此,所有的远程方法都必须抛出RemoteException。RMI服务是典型的面向接口编程,只有在远程接口里定义的方法才会作为远程服务。
下面是远程服务提供类,远程服务提供类必须实现远程接口,并继承java.rmi.server.UnicastRemoteObject对象,继承该类能“暴露”远程服务。
//远程服务类,远程服务类必须继承UnicastRemoteObject,并实现Remote接口
public class ServerImpl extends UnicastRemoteObject implements Server
{
//远程服务类必须拥有构造器,且构造器必须抛出RemoteException异常
public ServerImpl()throws RemoteException
{
}
//实现Remote接口必须实现的方法
public String helloWorld(String name)throws RemoteException
{
return name + ", 您好!";
}
//实现Remote接口必须实现的方法
public Person getPerson(String name,int age)throws RemoteException
{
return new Person(name,age);
}
//下面是服务类的本地方法,不会“暴露”为远程服务。
public void info()
{
System.out.println(“我是本地方法”);
}
//下面提供程序入口,将远程类实例绑定为本机的服务。
public static void main(String[] args)throws Exception
{
//创建远程服务类实例
Server imp = new ServerImpl();
//注册远程服务的端口
LocateRegistry.createRegistry(1099);
//将远程服务实例绑定为远程服务。
Naming.rebind("rmi://:1099/fdf", imp);
}
}
远程服务类必须有构造器,即使找个构造器什么都不做。而且,构造器必须抛出RemoteException异常。将两个编辑好的源文件存盘,然后编译。对于使用RMI,仅仅编译还不够,还必须使用rmic命令编译服务类,编译服务类是为了生成stub和skeleton——查看存放class文件的地方,多了如下两个class文件:
q ServerImpl_Stub.class
q ServerImpl_Skel.class
这两个类就是服务类生成的stub和skeleton。客户端程序面向接口编程,客户端部分需要Server接口的class文件,还需要stub文件。客户端的源代码如下:
public class RMIClient
{
//主方法,程序入口
public static void main(String[] args)throws Exception
{
//通过JNDI查找远程服务
Server ser = (Server)Naming.lookup("rmi://:1099/fdf");
//调用远程方法
System.out.println(ser.helloWorld("yeeku"));
//调用远程方法。
System.out.println(ser.getPerson("yeeku",28));
}
}
从客户端程序看,无法感受到Server的实现在远端。程序调用Server实例方法时,与平常调用方法只有非常细微的区别:调用远程方法必须抛出RemoteException。
再看远程方法的返回值,helloWorld的返回值是String,而getPerson的返回值则是Person对象。远程方法的返回值必须有一个要求:实现Serializable接口。因为远程的方法的参数、返回值都必须在网络上传输,网络只能传输字节流,因此,要求参数、返回值都可以转换成字节流——即实现序列化。主程序的如下一行代码,用于注册远程服务端口:
LocateRegistry.createRegistry(1099);
1099是RMI服务的默认端口。然后执行如下代码绑定远程服务
Naming.rebind("rmi://:1099/fdf", imp);
客户端使用JNDI查找,查找远程服务名。将远程服务对象类型转换成远程接口类型,客户端面向接口编程。RMI的具体实现,依然是依赖于底层的Socket编程。RMI依赖于TCP/IP传输协议,服务器端skeleton建立ServerSocket监听请求,而客户端建立Socket请求连接。RMI实现了网络传输的多线程、IO等底层细节。这些细节的实现就隐藏在rmic命令的执行中:使用rmic命令编译时生成的两个class文件:
q stub:该文件用于与客户端交流,建立Socket请求连接。
q skeleton:该文件用于与服务器端交流,建立ServerSocket监听请求。
RMI的原理示意如图所示。
[img]http://dl.iteye.com/upload/attachment/475603/df9beaeb-5fb6-3ee0-a716-75e1e3db0f5c.jpg[/img]
RMI是基于Java的分布式编程模型,使用RMI进行远程方法调用时,无须考虑方法底层的网络传输细节。下面使用RMI的示例程序:
先编写RMI服务器端,RMI需要通过远程接口“暴露”服务。也就是说,所有想被客户机调用的方法都必须在Remote接口里声明,否则无法完成调用。远程接口如下:
远程接口必须集成java.rmi.Remote接口
public interface Server extends Remote
{
//所有在Remote接口里声明的方法都必须抛出RemoteException异常
String helloWorld(String name) throws RemoteException;
Person getPerson(String name,int age)throws RemoteException;
}
远程接口必须继承java.rmi.Remote接口。远程接口里声明的方法会通过网络传输,而网络是不可靠的,因此,所有的远程方法都必须抛出RemoteException。RMI服务是典型的面向接口编程,只有在远程接口里定义的方法才会作为远程服务。
下面是远程服务提供类,远程服务提供类必须实现远程接口,并继承java.rmi.server.UnicastRemoteObject对象,继承该类能“暴露”远程服务。
//远程服务类,远程服务类必须继承UnicastRemoteObject,并实现Remote接口
public class ServerImpl extends UnicastRemoteObject implements Server
{
//远程服务类必须拥有构造器,且构造器必须抛出RemoteException异常
public ServerImpl()throws RemoteException
{
}
//实现Remote接口必须实现的方法
public String helloWorld(String name)throws RemoteException
{
return name + ", 您好!";
}
//实现Remote接口必须实现的方法
public Person getPerson(String name,int age)throws RemoteException
{
return new Person(name,age);
}
//下面是服务类的本地方法,不会“暴露”为远程服务。
public void info()
{
System.out.println(“我是本地方法”);
}
//下面提供程序入口,将远程类实例绑定为本机的服务。
public static void main(String[] args)throws Exception
{
//创建远程服务类实例
Server imp = new ServerImpl();
//注册远程服务的端口
LocateRegistry.createRegistry(1099);
//将远程服务实例绑定为远程服务。
Naming.rebind("rmi://:1099/fdf", imp);
}
}
远程服务类必须有构造器,即使找个构造器什么都不做。而且,构造器必须抛出RemoteException异常。将两个编辑好的源文件存盘,然后编译。对于使用RMI,仅仅编译还不够,还必须使用rmic命令编译服务类,编译服务类是为了生成stub和skeleton——查看存放class文件的地方,多了如下两个class文件:
q ServerImpl_Stub.class
q ServerImpl_Skel.class
这两个类就是服务类生成的stub和skeleton。客户端程序面向接口编程,客户端部分需要Server接口的class文件,还需要stub文件。客户端的源代码如下:
public class RMIClient
{
//主方法,程序入口
public static void main(String[] args)throws Exception
{
//通过JNDI查找远程服务
Server ser = (Server)Naming.lookup("rmi://:1099/fdf");
//调用远程方法
System.out.println(ser.helloWorld("yeeku"));
//调用远程方法。
System.out.println(ser.getPerson("yeeku",28));
}
}
从客户端程序看,无法感受到Server的实现在远端。程序调用Server实例方法时,与平常调用方法只有非常细微的区别:调用远程方法必须抛出RemoteException。
再看远程方法的返回值,helloWorld的返回值是String,而getPerson的返回值则是Person对象。远程方法的返回值必须有一个要求:实现Serializable接口。因为远程的方法的参数、返回值都必须在网络上传输,网络只能传输字节流,因此,要求参数、返回值都可以转换成字节流——即实现序列化。主程序的如下一行代码,用于注册远程服务端口:
LocateRegistry.createRegistry(1099);
1099是RMI服务的默认端口。然后执行如下代码绑定远程服务
Naming.rebind("rmi://:1099/fdf", imp);
客户端使用JNDI查找,查找远程服务名。将远程服务对象类型转换成远程接口类型,客户端面向接口编程。RMI的具体实现,依然是依赖于底层的Socket编程。RMI依赖于TCP/IP传输协议,服务器端skeleton建立ServerSocket监听请求,而客户端建立Socket请求连接。RMI实现了网络传输的多线程、IO等底层细节。这些细节的实现就隐藏在rmic命令的执行中:使用rmic命令编译时生成的两个class文件:
q stub:该文件用于与客户端交流,建立Socket请求连接。
q skeleton:该文件用于与服务器端交流,建立ServerSocket监听请求。
RMI的原理示意如图所示。
[img]http://dl.iteye.com/upload/attachment/475603/df9beaeb-5fb6-3ee0-a716-75e1e3db0f5c.jpg[/img]