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几大核心技术之一,仍应用在很多框架中。