设计模式(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 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 {

	@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");
		}
	}
}

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

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

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

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



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值