Java.rmi.ServerException: 服务器线程中发生远程异常 (ClassNotFoundException)。

13
以下方法:
    private void startServer() { // snippet that starts the server on the local machine
        try {
             RemoteMethodImpl impl = new RemoteMethodImpl();
             Naming.rebind( "Illusive-Server" , impl );
        } catch(Exception exc) {
            JOptionPane.showMessageDialog(this, "Problem starting the server", "Error", JOptionPane.ERROR_MESSAGE);
            System.out.println(exc);
        }
    }

抛出此异常:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
        java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
        java.lang.ClassNotFoundException: Interfaces.RemoteMethodIntf

当我启动我的项目时,弹出一个JOptionPane消息提示开启服务器时遇到问题,然后出现上述异常。这是什么原因呢?

我不明白为什么异常的最后一条语句说类未找到,而我已经导入了正确的包

5个回答

15

这个异常有四种情况:

  1. 在导出时:您没有运行“rmic”,也没有按照UnicastRemoteObject Javadoc序言中描述的步骤使其无需运行。

  2. 在绑定时:注册表(Registry)没有存根(stub)、远程接口或某些它们依赖的东西在其类路径上。

  3. 在查找时:客户端没有这些东西在其类路径上。

  4. 在调用远程方法时:您向服务器发送的是一个不在其CLASSPATH上的类,或者从服务器(包括异常)接收到的是一个不在您的CLASSPATH上的类,两种情况可能是远程接口方法签名中提到的一个类或接口的派生类或接口实现。

这是第2种情况。注册表(Registry)找不到指定的类。

有几种解决方案:

  1. 使用包含相关JAR文件或目录的CLASSPATH启动Registry。

  2. 通过LocateRegistry.createRegistry() 在您的服务器JVM中启动Registry。

  3. 使用动态存根,如UnicastRemoteObject Javadoc序言中所述。但是,可能仍然会遇到远程接口本身或其依赖项中的类相同的问题,在这种情况下,上述解决方法1-3仍适用于该类/这些类。

  4. 确保不发生上述第(4)种情况。

  5. 使用代码库特性。这实际上是一种部署选项,在初始开发阶段应该避免使用。


@SuhailGupta 看一下 java.rmi.server.UnicastRemoteObject 的前言。你只有在使用 Java < 1.5 或者不遵循这些指南时才需要生成存根类。因为你得到了那个异常,显然其中之一是真的。 - user207421
我正在使用Java > 1.5,并且我的类扩展了UnicastRemoteObject。我有点过时了吗?这是我使用的小代码 - Suhail Gupta
@SuhailGupta 请查看我引用的文档。只要在导出时引用一个端口号,即使是零,您就不需要存根。 - user207421
@EJP,我通过使用export CLASSPATH=myLib.jar:myLib2.jar设置CLASSPATH,在启动rmiregistry之前使我的工作正常。但是我正在进行生产部署,并注意到您在另一篇帖子中关于CLASSPATH仅适用于开发,而代码库配置更适合部署的评论。您是否理解应该清除部署的CLASSPATH,并且所有使用rmi的服务器和客户端都将在部署配置中使用代码库? - simgineer
@simgineer,这不是我说的。我并没有提到CLASSPATH是“用于开发”的,我说代码库特性是一种部署选项,不应阻碍开发。我其实并不是它的粉丝,但它偶尔有一些用途。但大多数RMI系统不需要这些用途,只需使用CLASSPATH即可。 - user207421

5
Remote Server Error:RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: mathInterface

这个错误很容易解决,只需按照以下步骤操作:

  • 例如,你的Java文件在D盘上
  • 启动rmiregistry D驱动器(例如D:\ start rmiregistry),不要在其他驱动器上启动rmiregistry,否则会出现上述错误。

(无论你的文件在哪里,都要启动rmiregistry


哇,这对我真的起作用了...虽然我不知道为什么?有人能解释一下这里发生了什么吗? - ultrajohn
1
@ultrajohn 看一下我的答案。这个可以工作是因为它将所需类放在注册表的CLASSPATH中,但仅当该类不在包中时才能起作用。如果是,它就不能工作了。缺乏解释和特殊情况的解决方案减1分。 - user207421

1
我将尽可能清晰地解释我所做的事情: 1. 首先,我像下面这样声明了classpath变量:
  1. set classpath=%classpath%
  2. set classpath=C:\compiler
  3. set classpath=C:\compiler\libro\cap07\rmi\hello\Hello.java
  4. set classpath=C:\compiler\libro\cap07\rmi\hello\Server.java
  5. set classpath=C:\compiler\libro\cap07\rmi\hello\Client.java
  • (所有在一行中设置:)

set classpath=%classpath%;C:\compiler;C:\compiler\libro\cap07\rmi\hello\Hello.java;C:\compiler\libro\cap07\rmi\hello\Server.java;C:\compiler\libro\cap07\rmi\hello\Client.java)

  • (我不确定是否需要.java文件,但我也为了避免疑虑写了它们。)

第二步。我使用以下命令编译代码:javac -d C:\compiler Hello.java Server.java Client.java,其中C:\compiler是根目录,就像Eclipse IDE上的src文件夹。

第三步。我运行了start rmiregistry命令。(无论在哪里运行它,效果都一样)。

第四步。我运行了以下命令:

start java -classpath C:\compiler -Djava.rmi.server.codebase=file:C:\compiler/ libro.cap07.rmi.hello.Server

你已经知道C:\compiler,但你需要在最后定义包地址,以便命令可以找到.class文件。打开任何一个.java文件并复制包地址,不包括包句。当你打开src目录(在我的情况下是C:\ compiler)时,你会发现所有的目录序列都已创建。当这个命令行正确创建时,无论你在哪里运行它,C:,D:,src,任何地方都可以运行。

第五步。最后,我用以下命令运行Client类:

java -classpath C:\compiler libro.cap07.rmi.hello.Client

总之,如果classpath变量未被创建或者创建错误,或者第4点的句子没有得到良好处理,JVM会抛出相同或类似的错误。去那里搜索!(抱歉我的英语)。

1
你不需要将 .java 文件放入 CLASSPATH 中,而是需要将包含 .class 文件或 JAR 文件的目录放入其中。显然,你所需的只是 C:\compiler 目录。 - user207421

-1

我遇到了同样的问题,但是不同的解决方案对我起作用。我正在运行两个不同的IntelliJ项目,并在每个项目中复制接口。其中一个在包中,另一个则没有,在这里引起了错误。

解决方案:

  • 确保接口副本不在包中。
  • 确保接口副本具有完全相同的包名称。

-1

你可以从任何地方启动rmiregistry,但必须确保编译后的类已经在你的类路径中。例如:

E:\ARMSRemoteUpdater\WebContent\WEB-INF\classes>set classpath=%classpath%;E:\ARMSRemoteUpdater\WebContent\WEB-INF\classes <ENTER>

E:\ARMSRemoteUpdater\WebContent\WEB-INF\classes>c: <ENTER>

C:\>rmiregistry

以上应该可以正常工作。

一般来说,如果您从编译类的根位置启动rmiregistry(在上面的示例中为E:\ ARMSRemoteUpdater \ WebContent \ WEB-INF \ classes),那么它将起作用,因为.(点-当前目录)已经设置在您的类路径中。

但是,一旦您从类路径中删除.(点-当前目录),上述工作条件也将失败。

希望我已经详细解释了。


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