我是否需要客户端,服务器和注册表上的所有类才能使用RMI?

我正在用RMI做我的第一步,我有一个简单的问题。

我有一个.jar文件,它有一个库中的几个方法的实现。 我想使用RMI在.jar文件中调用此方法。

我正在尝试创建一种包装来做它。

所以,我正在做这样的事情:

接口类 :此接口具有远程对象要实现的方法。

实现类 :这个类,具有接口方法的实现,每个实现调用.jar文件中的相应方法。 例如,jar文件有一个名为getDetails()的方法,它返回一个“ResponseDetail”对象。 ResponseDetail是我在.jar中的响应类。

服务器类 :它将方法绑定到rmiregistry

客户端类 :它将使用实现中实现的方法。

到现在为止还挺好? 🙂

现在,我有一个lib文件夹,其中包含.jar文件。

服务器机器中,我已经部署了Interface,Implementation和Server类。 我已经生成了存根,并成功运行了rmiregistry,但是,有了这些细节:

要启动rmiregistry,我必须在命令行中设置类路径以引用.jar文件,否则我得到java.lang.NoClassDefFoundError。 我用这个.sh文件做到了:

THE_CLASSPATH= for i in `ls ./lib/*.jar` do THE_CLASSPATH=${THE_CLASSPATH}:${i} done rmiregistry -J-classpath -J".:${THE_CLASSPATH}" 

要启动服务器,我还必须设置类路径以引用.jar文件,否则,我得到java.lang.NoClassDefFoundError。 我用过这样的东西:

 THE_CLASSPATH= for i in `ls ./lib/*.jar` do THE_CLASSPATH=${THE_CLASSPATH}:${i} done java -classpath ".:${THE_CLASSPATH}" Server 

客户端机器:要从客户端机器运行Client.class文件,我必须将.jar文件复制到它,并在类路径中引用它们,否则,它不会运行,我得到java.lang。 NoClassDefFoundError错误。 我不得不在客户端机器上使用它:

 THE_CLASSPATH= for i in `ls ./lib/*.jar` do THE_CLASSPATH=${THE_CLASSPATH}:${i} done java -classpath ".:${THE_CLASSPATH}" HelloClient 

这个可以吗? 我的意思是,我是否必须将.jar文件复制到客户端计算机以通过RMI执行方法?

在JDK v5之前,必须使用rmicRMI编译器 )生成RMI存根。 这是从JDK v5自动完成的。 此外,您还可以从Java代码中启动RMI注册表。 要从简单的RMI应用程序开始,您可能需要按照以下步骤操作:

  1. 创建界面:
     import java.rmi.*; public interface SomeInterface extends Remote { public String someMethod1() throws RemoteException; public int someMethod2(float someParameter) throws RemoteException; public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException; } 
  2. 实现界面:
     import java.rmi.*; import java.rmi.server.*; public class SomeImpl extends UnicastRemoteObject implements SomeInterface { public SomeImpl() throws RemoteException { super(); } public String someMethod1() throws RemoteException { return "Hello World!"; } public int someMethod2( float f ) throws RemoteException { return (int)f + 1; } public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException { int i = someStruct.getInt(); float f = someStruct.getFloat(); someStruct.setInt(i + 1); someStruct.setFloat(f + 1.0F); return someStruct; } } 
  3. 实现要在客户端和服务器之间传递的非原始可序列化对象:
     import java.io.*; public class SomeStruct implements Serializable { private int i = 0; private float f = 0.0F; public SomeStruct(int i, float f) { this.i = i; this.f = f; } public int getInt() { return i; } public float getFloat() { return f; } public void setInt(int i) { this.i = i; } public void setFloat(float f) { this.f = f; } } 
  4. 实现服务器:
     import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.net.*; import java.io.*; public class SomeServer { public static void main(String args[]) { String portNum = "1234", registryURL; try{ SomeImpl exportedObj = new SomeImpl(); startRegistry( Integer.parseInt(portNum) ); // register the object under the name "some" registryURL = "rmi://localhost:" + portNum + "/some"; Naming.rebind(registryURL, exportedObj); System.out.println("Some Server ready."); } catch (Exception re) { System.out.println("Exception in SomeServer.main: " + re); } } // This method starts a RMI registry on the local host, if it // does not already exist at the specified port number. private static void startRegistry(int rmiPortNum) throws RemoteException{ try { Registry registry = LocateRegistry.getRegistry(rmiPortNum); registry.list( ); // The above call will throw an exception // if the registry does not already exist } catch (RemoteException ex) { // No valid registry at that port. System.out.println("RMI registry is not located at port " + rmiPortNum); Registry registry = LocateRegistry.createRegistry(rmiPortNum); System.out.println("RMI registry created at port " + rmiPortNum); } } } 
  5. 实施客户:
     import java.io.*; import java.rmi.*; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; public class SomeClient { public static void main(String args[]) { try { String hostName; String portNum = "1234"; String registryURL = "rmi://localhost:" + portNum + "/some"; SomeInterface h = (SomeInterface)Naming.lookup(registryURL); // invoke the remote method(s) String message = h.someMethod1(); System.out.println(message); int i = h.someMethod2(12344); System.out.println(i); SomeStruct someStructOut = new SomeStruct(10, 100.0F); SomeStruct someStructIn = new SomeStruct(0, 0.0F); someStructIn = h.someStructTest(someStructOut); System.out.println( someStructIn.getInt() ); System.out.println( someStructIn.getFloat() ); } catch (Exception ex) { ex.printStackTrace(); } } } 

较大的客户端 – 服务器应用程序应分为三个模块: clientservercommon (对于服务器和客户端代码之间共享的类,即本示例中的远程接口和非基本对象)。 然后,将从类路径上的client + common模块创建客户端应用程序,并从类路径上的server + common模块创建server

多年前我用这个例子来学习RMI的基础知识,它仍然有效。 然而,它远非完美(使用默认的Java包,不正确的exception处理,主机名和端口参数是硬编码的,不可配置等)

尽管如此,对初学者来说还是不错的。 所有文件都可以放在一个目录中,并使用简单的javac *.java命令进行javac *.java 。 然后,可以使用java SomeServer和客户端启动服务器应用程序,方法是启动java SomeClient命令。

我希望这有助于理解Java RMI,事实上,它远比这复杂得多。

你不应该生成存根(如果你正在学习教程,那就太老了)。 你可以运行客户端,而不必在本地使用jar(使用远程类加载),但是使用本地可用的jar这样做更容易(我个人做了很多RMI,从未实际部署过具有远程类加载的系统) )。 通常,您需要2个jar,一个只包含远程接口的“客户端”jar(以及这些接口使用的任何Serializable类)和一个包含实现类的“服务器”jar。 然后,您将使用服务器jar运行服务器,使用客户端jar运行rmiregistry / client。

这是一个非常好的(最新的和简单的) 入门指南 。

简而言之,其他答案的详细说明如下:

  • 客户端只需要公共接口(和客户端类),而不需要服务器实现。

  • 服务器需要接口和实现(以及您的服务器主类)。

  • rmir​​egistry只需要接口。

    (实际上,您可以在服务器进程内部启动自己的注册表 – 然后根本不需要rmiregistry 。请查看java.rmi.registry.LocateRegistry类中的createRegistry方法。)

“接口”在这里表示远程接口和这些作为参数或参数类型使用的任何(可序列化)类。

如何将这些类分发到jar文件与此无关。