RMI(远程方法调用)简单示例

一、故事情景

小王花了4000元钱报名跟着万教练学车。教练烟瘾来了,正好身上的烟也抽光了。

1、情景一

万教练自己不辞辛苦,去商店买烟。临走时还不忘叮嘱小王好好练车。

2、情景二

万教练和小王说,你去给我买包烟,回来你多练一个小时。小王十分感激,屁颠屁颠的去了。

3、情景三

小王为了逃避给教练买烟就不去驾校了。谁知教练神通广大,竟然买通了“上帝”,可以通过“上帝”控制小王的行为。

二、抽象建模

很明显上面的故事中提到了两个对象,教练和小王。创建两个类。

public class JiaoLian{}
public class XiaoWang{}

三、情景分析

1、情景一分析

教练自己去买烟,很明显可以采用如下的实现方式。

public class JiaoLian
{
    public void buyTobacco()
    {
        System.out.println("好好练车啊,我去买包烟");
        System.out.println("买烟路上。。。");
    }
}

而此时小王就此事什么都不用做。

2、情景二分析

此时教练自己不去买烟,而是让小王去。相当于教练调用小王的方法。

public class JiaoLian
{
    public void buyTobacco()
    {
        XiaoWang xw = new XiaoWang();
        xw.buyTobacco();
    }
}

public class XiaoWang
{
    public void buyTobacco()
    {
        System.out.println("教练我去了,回来让我多练一会儿啊");
    }
}

3、情景三分析

这招很厉害,小王无奈只好就范。因为小王的所有技能都是需要向“上帝”报备的。

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("玉溪");
    }
}

六、结束语

仅以此文纪念我的学车时光,请勿对号入座。如有雷同纯属巧合。

你可能感兴趣的:(Java)