Java中远程对象取消导出时出现NoSuchObjectException问题
问题分析与解决
核心原因
你遇到的NoSuchObjectException是因为你传给unexportObject的不是本地导出的服务实例,而是从注册表lookup得到的远程stub对象。
UnicastRemoteObject.unexportObject要求传入的是在当前JVM中被导出的实际对象实例,但你在closeDataServ里调用this.getDataServ(),返回的是RMI注册表返回的远程引用(stub)——这个stub并不是当前JVM里被导出的那个真实对象,自然会抛出“找不到该导出对象”的异常。
为什么数据库服务没出问题?
因为数据库服务和注册表在同一个JVM里运行。当你在同一个JVM中lookup自己绑定的UnicastRemoteObject时,RMI不会返回stub,而是直接返回本地的服务实例对象。这时候调用unexportObject传入这个本地实例,自然能正常执行。
而auth/management服务在独立的JVM,客户端回调也在各自的JVM,它们的close方法里lookup回来的都是远程stub,所以触发异常。
解决方案
修改代码,在本地保存每个服务的实际实例引用,不要依赖lookup获取要取消导出的对象:
- 给注册表管理类添加成员变量,保存本地服务实例:
// 新增成员变量,保存本地导出的服务实例 private ServicioDatosInterface localDataServ; private AuthServiceInterface localAuthServ; private ManagementServiceInterface localManagementServ; // 客户端回调同理,保存本地的callback实例
- 在launch方法中,绑定后把传入的实例赋值给本地成员变量:
public String launchDataServ(ServicioDatosInterface dataServ) throws RegException { try { this.reg.bind(this.dataServURL, dataServ); // 保存本地实例引用 this.localDataServ = dataServ; } catch (RemoteException e) { throw new RegException("error message 1"); } catch (AlreadyBoundException e) { throw new RegException("error message 2"); } return this.dataServURL; }
- 在close方法中,使用本地保存的实例来执行unexport:
public void closeDataServ() throws RegException { try { // 使用本地保存的实例,而不是lookup的结果 UnicastRemoteObject.unexportObject(this.localDataServ, true); this.reg.unbind(this.dataServURL); // 清空引用 this.localDataServ = null; } catch (RemoteException e) { throw new RegException("error message 1"); } catch (NotBoundException e) { throw new RegException("error message 2"); } }
额外说明
注释掉unexportObject后程序挂起,是因为RMI的导出对象会持有非守护线程,阻止JVM退出——这正好说明本地的服务实例确实处于导出状态,只是你之前用错了对象去取消导出。
内容的提问来源于stack exchange,提问作者jack
相关产品推荐
相关产品推荐

