首页 > 代码库 > 设计模式(11)--代理模式之RMI
设计模式(11)--代理模式之RMI
(1)确定变量和返回值是属于原语(primitive)类型或者可序列化的(Seriailizable)类型。原语类型 都实现了Seriailizable接口。
(2)transient 关键字,告诉JVM不要序列化这个字段。
动态类下载(dynamic class downloading )
代理模式 :控制对象的访问
代理模式有很多种。如:远程代理,虚拟代理。动态代理(保护代理)
客户调用本地的方法,本地的方法调用远程的方法。 本地方法就是“代理”。“代理”处理所有网络通信的低层细节。
Java 内置远程调用功能:RMI
RMI的好处:
不必写任何网络或I/O代码。
客户程序调用远程方法(即真正的服务所在)就和在运行本地JVM上对象进行正常方法调用一样。
lookup service 这个服务用来寻找和访问远程对象。
注意:I/O 和网络 是有网险的,容易失败,所以必须随时抛出异常。
---------------------远程调用实现步骤---------------------
RMI 会产生客户端辅助对象(称为stub(桩)命名格式类名_Stub.class)和服务端辅助对象(称为skeleton(骨架)命名格式类名_Skel.class)。
一、制作远程服务(远程端)
(1)制作远程接口。 接口定义了让客户远程调用的方法。
1.扩展java.rmi.Remote Remote是一个"记号“接口,不具有方法,对于RMI来说Remote具有特别的意义。
public interface MyRemote extends Remote{} //表示此接口用来扶持远程调用
2.声明所有方法都抛出RemoteException.
<span style="white-space:pre"> </span>import java.rmi.*; <span style="white-space:pre"> </span>public interface MyRemote extends Remote{ <span style="white-space:pre"> </span>public String sayHello() throws RemoteException; <span style="white-space:pre"> </span>}3.确定变量和返回值属于原语(primitive)类型或者可序列化(Serializable)类型
远程方法的变量必须被打包并通过网络运送,这要靠序列化来完成。
(2)制作远程接口实现。
1.public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
//sayHello的实现
}
2.扩展UnicastRemoteOject .为了成为远程服务对象,你的对象需要某些”远程的“功能,最简单的方式实现此类。
3.设计一个不带变量的构造器,并声明RemoteException
<span style="white-space:pre"> </span>/** <span style="white-space:pre"> </span>新超类UnicassRemoteObject带来一个小问题:它的构造器抛出RemoteException.唯一解决这个问题的方法就是此类也声明此异常。 <span style="white-space:pre"> </span>如果超类的构造器抛出异常,那么子类的构造器也必须抛出异常。 <span style="white-space:pre"> </span>由于调用子类构造的时候先调用父类的构造,所以父类抛出的异常要小于等于子类抛出的异常 <span style="white-space:pre"> </span>*/ <span style="white-space:pre"> </span>public MyRemoteImpl() throws RemoteException4.用RMI Registry注册此服务。 放在mian()方法中。 等价于第5步。
<span style="white-space:pre"> </span>让它可以被远程客户调用,将此服务实例化,然后放进RMI registry中(记得先确定rmi registry 正在运行,否则注册会失败)。当注册这个对象时,RMI系统其实注<span style="white-space:pre"> </span>册的是stub,因为这是客户真正需要的。注册服务使用java.rmi.Naming类的静态rebind()方法。 <span style="white-space:pre"> </span>try{ <span style="white-space:pre"> </span>MyRemote service = new MyRemoteImpl(); <span style="white-space:pre"> </span>Naming.rebind("RemoteHello",service); //为服务命名,好让客户在注册表中寻找。 <span style="white-space:pre"> </span>}catch(Exception ex){...}
(3)利用rmic产生stub和skeleton。 JDK中有此工具 %rmic MyRemoteIMpl
在远程实现类上执行rmic
(4)启动RMI regisgry(rmiregistry). rmiregisty就像电话簿,客户可以从中查到代理的位置。 %rmiregistry --开启一个终端,启动rmiregistry 确定启动目录必须可以访问你的类。“classes”目录启动。
(5)开启远程服务 %java MyServiceImpl
二、客户端如何得到Stub对象。
客户必须取得stub对象(我们的代理)以调用其中的方法。所以我们需要RMI Registry的帮忙。客户从Registry中寻找(lookup)代理。
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/RemoteHello"); //"RemoteHello" 远程端注册时使用的名字。
三、工作方式。
(1)客户到registry中寻找。Naming.lookup("rmi://127.0.0.1/RemoteHello");
(2)RMI registry 返回stub对象。 (作为lookup方法的返回值)然后RMI会自动对stub反序列化。你在客户端必须有stub类(由rmic)为你产生,否则stub就无法被反序列化。
(3)调用stub的方法,就像stub就是真正的服务对象一样。
客户端完整代码
import java.rmi.Naming; public class MyRemoteClient { public static void main(String[] args) { new MyRemoteClient().go(); } public void go(){ try{ MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/RemoteHello"); String s = service.sayHello(); System.out.println(s); }catch(Exception e){ e.getMessage(); } } }
需要注意的是 注册服务前 需要开启一个命令窗口执行 rmiregstry 命令 此命令在 看得见 远程类的路径下执行。
远程接口完完整代码
import java.rmi.Remote; import java.rmi.RemoteException; public interface MyRemote extends Remote{ public String sayHello() throws RemoteException; }远程实现完整代码
import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote { @Override public String sayHello() throws RemoteException { return "Server says "; } public MyRemoteImpl() throws RemoteException{} public static void main(String[] args) { try { MyRemote service = new MyRemoteImpl(); Naming.rebind("RemoteHello", service); } catch (Exception e) { e.printStackTrace(); System.out.println("111"); } } }