小王花了4000元钱报名跟着万教练学车。教练烟瘾来了,正好身上的烟也抽光了。
万教练自己不辞辛苦,去商店买烟。临走时还不忘叮嘱小王好好练车。
万教练和小王说,你去给我买包烟,回来你多练一个小时。小王十分感激,屁颠屁颠的去了。
小王为了逃避给教练买烟就不去驾校了。谁知教练神通广大,竟然买通了“上帝”,可以通过“上帝”控制小王的行为。
很明显上面的故事中提到了两个对象,教练和小王。创建两个类。
public class JiaoLian{}
public class XiaoWang{}
教练自己去买烟,很明显可以采用如下的实现方式。
public class JiaoLian
{
public void buyTobacco()
{
System.out.println("好好练车啊,我去买包烟");
System.out.println("买烟路上。。。");
}
}
而此时小王就此事什么都不用做。
此时教练自己不去买烟,而是让小王去。相当于教练调用小王的方法。
public class JiaoLian
{
public void buyTobacco()
{
XiaoWang xw = new XiaoWang();
xw.buyTobacco();
}
}
public class XiaoWang
{
public void buyTobacco()
{
System.out.println("教练我去了,回来让我多练一会儿啊");
}
}
这招很厉害,小王无奈只好就范。因为小王的所有技能都是需要向“上帝”报备的。
public class JiaoLian
{
public void buyTobacco()
{
XiaoWang xw = getCtrlFromGod();
xw.buyTobacco();
}
}
情景一相当于只有一个对象,该对象调用自己的方法实现自己的需求。情景二相当于有两个对象,通过调用另外一个对象的方法来实现自己的需求。这两种情景都属于本地方法调用,不过情景二中由其他对象负责实现该方法,有利于避免某个对象拥有的方法大而杂。在情景三中,服务提供者(小王)向注册中心(上帝)注册服务,服务调用者(教练)通过注册中心调用服务。这样服务的提供者与调用者就可以分别布置在不同的机器上。
使用RMI实现上面的情景三。首先定义小王的服务,将其定义为接口,实现定义与实现的分离。需要继承java.rmi.Remote类,提供的方法要抛出RemoteException(否则在注册服务时会报参数异常,无法注册成功)。
public interface XiaoWangService extends Remote
{
public String buy(String name) throws RemoteException;
}
创建类实现上面的接口。继承java.rmi.server.UnicastRemoteObject的目的是使该类可以被序列化,拥有序列号,且满足注册的条件。
public class XiaoWangServiceImpl extends UnicastRemoteObject implements XiaoWangService
{
private static final long serialVersionUID = 1L;
protected XiaoWangServiceImpl() throws RemoteException
{
super();
}
@Override
public String buy(String name) throws RemoteException
{
System.out.println("购买 " + name + " 烟");
return "教练给你烟";
}
}
小王如何向上帝注册呢?创建一个服务的对象称为bone(骨架)。创建context,将bone与关键词”rmi://121.250.222.32:6666/xiaowangService”重新绑定。关于context的使用自行查询JNDI(Java命名与目录接口)。直接注册肯定不行,因为得上帝上班了才能够实现注册。找到XiaoWangService这个类的classpath(不要忽略了类的pacakge),然后打开命令行执行start rmiregistry 6666命令。如果classpath不对,上帝找不到(这个时候的上帝有点智障)。命令执行完,将弹出一个窗口。此时在执行下面的注册代码。
public class XiaoWang
{
public static void main(String[] args)
{
try
{
Context context = new InitialContext();
XiaoWangService myService = new XiaoWangServiceImpl(); context.rebind("rmi://121.250.222.32:6666/xiaowangService", myService);
}
catch (NamingException e)
{
e.printStackTrace();
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
}
那么问题又来了,教练如何调用呢?同样新建javax.naming.Context对象,执行查询返回Object强制类型转换成XiaoWangService。执行该方法时可以获得方法的返回值。服务方法中的输出只显示在服务方的控制台上。
public class JiaoLian
{
public static void main(String[] args)
{
Context context = new InitialContext();
XiaoWangService service = (XiaoWangService) context.lookup("rmi://121.250.222.32:6666/xiaowangService");
service.buy("玉溪");
}
}
仅以此文纪念我的学车时光,请勿对号入座。如有雷同纯属巧合。