今天在看代理设计模式,java中远程代理的实现一定会用到RMI的,很久没有温习过RMI的知识了,今天就重新过一遍这个知识点来让自己加深印象,构建一个简单的RMI小程序需要用到一下几个类:
- java.rmi.Remote
- java.rmi.RemoteException
- java.rmi.Naming
UML类图:
类图很简单,但是有几点需要注意:
- 服务器端的接口应该继承自java.rmi.server.UnicastRemoteObject类并实现java.rmi.Remote接口
- 由于是远程方法调用,有可能由于网络原因而出现异常,服务器端接口的方法应该抛出java.rmi.RemoteException
- 服务器端接口的具体实现类必须显示地声明一个无参构造方法并抛出RemoteException, 这是因为UnicastRemoteObject类的无参构造方法就抛出了RemoteException,这样在实例化MyRemoteImpl之前会先实例化UnicastRemoteObject
- 由于参数和返回值需要在网络中传播,如果你的参数和返回值不是java原始数据类型(例如自定义的对象),那么该对象需要实现java.io.Serializable, 这样JVM才能对该对象进行序列及反序列化
我们再来看看具体的实现:
MyRemote接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MyRemote extends Remote {
public String getServerTime(String employee) throws RemoteException;
}
MyRemoteImpl实现类
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
private static final long serialVersionUID = 6812592847867041499L;
public MyRemoteImpl() throws RemoteException {
super();
}
public static void main(String[] args) throws MalformedURLException, RemoteException, AlreadyBoundException {
MyRemote myRemote = new MyRemoteImpl();
Naming.bind("rmi://192.168.3.4:1099/myRemote", myRemote);
System.out.println("Ready...");
}
@Override
public String getServerTime(String employee) throws RemoteException {
return String.format("Hello %s, Italy time is: %s", employee, new Date());
}
}
客户端测试类:
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class RmiClient {
public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
MyRemote myRemote = (MyRemote) Naming.lookup("rmi://192.168.3.4:1099/myRemote");
System.out.println(myRemote.getServerTime("Linda"));
}
}
现在运行测试还为时尚早,为了实现跨JVM的远程方法调用我们还需要做一些必须的事情:
- 使用rmic工具生成Stub桩供远程调用(Java 1.5之后可以不用生成这个文件?还在研究)
- rmiregistry开放RMI注册
- 使用java.rmi.Naming的bind方法来绑定服务
我们一步一步来:
首先生成stub文件,客户端需要这个文件来调用远程对象,这个文件其实就是远程对象在本地的一个代理,客户端以为是跟远程对象直接打交道,其实客户端调用的是该客户服务对象,该桩又调用远程对象。
生成的桩文件:
运行rmiregistry命令:
运行MyRemoteImpl的main方法来将服务绑定到远程计算机的1099端口
我们写的是一个RMI程序,为了测试跨内存空间(跨JVM)的远程方法调用,我把服务器端的以下class文件放到了国外服务器上面:
- MyRemote.class
- MyRemoteImpl.class
- MyRemoteImpl_Stub.class
把以下客户端的class放到本地:
- MyRemote.class
- MyRemoteImpl_Stub.class
- RmiClient.class
现在让我来测试一下,运行命令:
java RmiClient
从运行结果可以看出来,本地客户端成功调用到了远在欧洲的对象并把String值返回到了本地通过控制台打印出来。下一个待解决的问题是如何可以不需要stub文件来进行远程方法调用,正在学习中。