1 远程代理的意义
远程代理为一个位于不同的地址空间的对象提供一个局域代表对象,这个不同的地址空间可以是在本机器中,也可以是在另一台机器中,远程代理还有个酷炫的名字:大使。
2 远程代理的结构
远程代理是代理模式的经典应用,类似客户端/服务器模式,是远程通信的一个缩影。示意图示如下:
3 代码示例:
我们需要对一家连锁店里的库存信息进行监控,以便准确的知道不同店的运行情况:
构建一个商店监视器,报告关于商店的位置和库存信息:
import java.rmi.RemoteException;
public class StoreMonitor {
RemoteStore store;
public StoreMonitor(RemoteStore store) {
this.store = store;
}
public void report() throws RemoteException {
System.out.println("Store location: " + store.reportLocation());
System.out.println("Store count: " + store.reportCount());
}
}
这里定义了一个RemoteStore,其实它是我们真实需要的一个代理:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteStore extends Remote {
public String reportLocation() throws RemoteException;
public int reportCount() throws RemoteException;
}
RemoteStore接口的一个实现,能够用来完成远程服务调用:
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class DefaultRemoteStore extends UnicastRemoteObject implements RemoteStore {
private static final long serialVersionUID = -6415918724751304895L;
private int count;
private String location;
public DefaultRemoteStore(int count, String location) throws RemoteException {
this.count = count;
this.location = location;
}
@Override
public String reportLocation() throws RemoteException {
return location;
}
@Override
public int reportCount() throws RemoteException {
return count;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Remote location is: ");
sb.append(location);
sb.append("\n");
sb.append("Curret count is: ");
sb.append(count);
return sb.toString();
}
}
下面是服务器端绑定的代码,即服务器那边将自己的信息注册以便能提供服务:
import java.rmi.Naming;
public class StoreTestDriver {
public static void main(String[] args) {
DefaultRemoteStore store = null;
int count;
if (args.length < 2) {
System.out.println("...");
System.exit(1);
}
try {
count = Integer.parseInt(args[1]);
store = new DefaultRemoteStore(count, args[0]);
Naming.rebind("//" + args[0] + "/store", store);
} catch (Exception e) {
e.printStackTrace();
}
}
}
真实客户端的调用代码:
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class MonitorTestDriver {
public static void main(String[] args) throws MalformedURLException, RemoteException,
NotBoundException {
String[] locations = { "rmi://localhost/store" };
StoreMonitor[] monitor = new StoreMonitor[locations.length];
for (int i = 0; i < locations.length; i++) {
RemoteStore reporter = (RemoteStore) Naming.lookup(locations[i]);
monitor[i] = new StoreMonitor(reporter);
System.out.println(monitor[i]);
}
for (int i = 0; i < monitor.length; i++) {
monitor[i].report();
}
}
}
代码很简单,下面说一下运行:
首先需要编译远程的服务:
>>rmic DefaultRemoteStore
有一点需要注意的是,如果是src和bin分开存放的话,rmic是针对编译之后的class文件的,此处将产生DefaultRemoteStore _Stub.class文件。
服务注册:
>>rmiregistry
此处需要注意的是:DefaultRemoteStore和DefaultRemoteStore _Stub虽然在同一路径下,但是stub不会直接加载,而是由DefaultRemoteStore在向rmi注册时,要求rmiregistry去加载DefaultRemoteStore _Stub的,也就是说生成的stub是为rmiregistry所用的。因此在执行rmiregistry之前,需要设置classpath使的stub能被识别。最简单的方式是进入到stub所在的目录再执行rmiregistry,这里应用了classpath中的当前目录“.”.
服务端提供服务:
>>java StoreTestDriver localhost 100
类似的,还可以添加一些其他的。
监视:
>>java MonitorTestDriver
将能取得注册的信息。
RMI现在直接使用的不多,但作为J2EE几大核心技术之一,仍应用在很多框架中。