设计模式(11)--代理模式之RMI

来源:互联网 发布:java药品管理系统 编辑:程序博客网 时间:2024/05/22 10:23

(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 RemoteException
4.用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 {@Overridepublic 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");}}}

对于RMI 程序员常犯的三个错误:

1.忘了在启动远程服务之前先启动rmiregistry (要用Naming.rebind()注册服务,rmiregistry必须是运行的)

2.忘了让变量和返回值的类型成为可序列化的类型(这种错误无法在编译期发现,只会在运行时发现)。

3.忘了给客户提供stub类。



0 0