Java RMI - UnicastRemoteObject:UnicastRemoteObject.exportObject()和继承UnicastRemoteObject有什么区别?

19

我正在为一场考试做准备,有一个问题希望这里的某人能回答我。

它与RMI和远程对象有关。 我想知道这两个实现之间为什么有如此大的区别。 一个是扩展UnicastRemoteObject,而另一个是将对象导出为UnicastRemoteObject。

我真的不明白它们之间的区别

接口:

public interface EchoI extends Remote {
   public String echo() throws RemoteException
}

这是服务器代码(版本1):

public class EchoImpl extends UnicastRemoteObject implements EchoI {
    public EchoImpl {
        super();
    }

    public static void main (String[] args) {
        try {
            LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
            StoreHouse storehouseImpl = new StorehouseImpl();
            Naming.rebind("//localhost/StoreHouse.SERVICE_NAME", storehouseImpl);
            System.out.println("Server ready");
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    public String echo() {
        return "echo";
    }
}

这将是第二个版本:

public class EchoImpl implements EchoI {
    public static void main (String[] args) {
        EchoI echoService = new EchoImpl();
        EchoI stub = (EchoI) UnicastRemoteObject.exportObject(echoService, 0);
        Registry registry = LocateRegistry.getRegistry();
        registry.bind("echoService", stub);
        ...
    }
}

我的问题是:这两者之间有什么区别?

在第一个版本中,注册表被显式创建,而远程对象是在重新绑定内创建的吗?

我很好奇,为什么在第一个版本中我需要自己创建注册表,但不需要显式地导出对象,只需使用 Naming 就可以重新绑定它。那个对象是否已经绑定到注册表中,或者我可以使用 bind 来代替呢?如果对象以前没有绑定,那么执行重新绑定会发生什么?

在第二个版本中,注册表似乎已经被创建。

为什么将命名绑定与直接绑定到注册表相同?

这是我认为的:

  • 第一个类直接实现了 UnicastRemoteObject 接口,这意味着在运行时注册表会被创建,并且对象会自动导出到 RMI 注册表中。
  • 由于对象已经绑定到注册表中,因此必须进行重新绑定而不是正常绑定。
  • 后者都是显式完成的。
4个回答

22

这里有两个问题。

  1. 你可以扩展UnicastRemoteObject或调用UnicastRemoteObject.exportObject()。选择哪种方式取决于你。第一种方式简单自动;第二种方式意味着你可以扩展另一个类。

  2. 你可以使用外部RMI注册表,也可以在服务器JVM内部创建它。同样,你可以自行决定,两种方法都有各自的优点。

    这两个问题之间没有交互作用。

  3. 如果你extend UnicastRemoteObject,你还可以获得“远程语义”的hashCode()equals()方法的好处,这使得所有存根看起来都与导出它们的远程对象相同,但这对客户端来说没有实际用途,实际上只是为支持RMI实现而存在。


5
java.rmi.server.UnicastRemoteObject被用于将远程对象导出到Java远程方法协议(JRMP)中,并获取与远程对象通信的存根。对于以下构造函数和静态exportObject方法,将获取要导出的远程对象的存根...请参考Javadoc文档

顺便说一下,你的服务器代码版本1让我感到困惑,locateRegistry无法工作,因为它没有被声明。 - stacker
1
谢谢您的回复,但是它并没有真正为我澄清事情……您现在喜欢这段代码吗? - CatholicEvangelist
现在编译器更喜欢你的代码了,结果相同,对象已在RMI注册表中使用提供的名称注册,并可供客户端访问。唯一的区别是rebind比bind(如果已绑定)更具容错性。我会点赞你的问题以吸引更多人。 - stacker
再次感谢 - 所以第一个版本相对更安全,对吧?在几乎所有的教程中,RMI对象都是用后一种方式导出的,但我个人更喜欢第一个版本。 - CatholicEvangelist
如已提到,它们是等效的,区别在于重新绑定不会抛出AlreadyBoundExeption,我也更喜欢你的第一个选项。在您的考试中,您可以描述一种方式,并提到还有另一种选项。 - stacker
显示剩余2条评论

0
首先,使用Naming类和Registry类绑定和重新绑定远程对象与类是否扩展UnicastRemoteObject无关。有关差异,请参阅这里
其次,类扩展UnicastRemoteObject的区别在于如果以该类型的对象作为存根,则您将不再需要调用UnicastRemoteObject.exportObject来获取存根以便与注册表绑定。在您的版本1中,StorehouseImpl必须扩展UnicastRemoteObject,实际上,对于您的版本1,EchoImpl没有必要扩展UnicastRemoteObject,因为没有任何EchoImpl实例被注册为远程对象到注册表中。
第三,您提到如果在执行bind之前执行rebind会发生什么。根据此处中的javadoc所解释的,如果没有插入关键字名称,它将表现得与第一次执行bind相同。

0
你可以在需要扩展其他类的情况下调用 UnicastRemoteObject.exportObject(),而不是继承 UnicastRemoteObject。我认为总体效果是相同的。

查看这个


1
质量差的链接,至少有两个主要错误。-1 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接