java rmi 动态加载远程代码
一、rmi为什么要加载远程代码
rmi在进行远程方法调用的时候需要客户端和服务端都准备好各自所需的类文件,当有很多个客户端对服务端代码进行远程调用的时候就需要维护每个客户端的本地类文件,非常的繁琐。
通过rmi的远程加载代码可以简化这个过程,我们将类文件集中存在注册服务的某个路径或者某个网址下,然后通过 -Djava.rmi.server.codebase 属性配置远程加载的代码路径,当客户端调用时可以自动的加载所需要的类文件。调用流程如下:
二、什么是codebase
codebase可以定义为将类加载到虚拟机的源或位置。例如,如果你邀请了一个新朋友过来吃饭,你就需要给他指明你住的地方,这样他或她就可以找到你的房子。类似地,您可以将codebase看作是给JVM的方向,这样它就可以找到需要的类文件。
您可以将CLASSPATH视为“本地codebase”,因为它是磁盘上加载本地类的位置列表。当从基于磁盘的本地源加载类时,会查询CLASSPATH变量。您的CLASSPATH可以设置为对目录和/或类文件的存档采用相对或绝对路径名。因此,正如CLASSPATH是一种“本地codebase”一样,远程对象使用的codebase也可以被认为是“远程codebase”。
三、如何配置
通过jvm的启动参数可以配置 -Djava.rmi.server.codebase 属性, -Djava.rmi.server.codebase 可以是URL、也可以是本地物理路径
-Djava.rmi.server.codebase=http://webvector/export/ #注意最后路径要带上/ -Djava.rmi.server.codebase=http://webline/public/mystuff.jar #类文件在jar包中 -Djava.rmi.server.codebase="http://webfront/myStuff.jar http://webwave/myOtherStuff.jar" #多个路径
-Djava.rmi.server.codebase=file:///D:\temp\jnet\out\production\jnet\ #物理文件路径
如果没有正确配置codebase,当我们注册服务时,会报如下错误:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Compiled Code) at sun.rmi.transport.StreamRemoteCall.executeCall(Compiled Code) at sun.rmi.server.UnicastRef.invoke(Compiled Code) at sun.rmi.registry.RegistryImpl_Stub.rebind(Compiled Code) at java.rmi.Naming.rebind(Compiled Code) at examples.callback.MessageReceiverImpl.main(Compiled Code) RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
四、示例
代码目录结构:
待调用的服务接口
package com.jnet.rmi.dynamicLoad; import java.io.IOException; import java.rmi.Remote; import java.util.Date; /** * @author Xunwu Yang 2021-01-10 * @version 1.0.0 */ public interface IHelloService { String echo(String message) throws IOException; Date time() throws Exception; }
组合接口
package com.jnet.rmi.dynamicLoad; import java.rmi.Remote; /** * @author: yangxunwu * @date: 2021/1/11 16:45 */ public interface IRemoteHelloService extends IHelloService, Remote { }
接口实现
package com.jnet.rmi.dynamicLoad.impl; import com.jnet.rmi.dynamicLoad.IRemoteHelloService; import java.io.IOException; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.Date; /** * @author: yangxunwu * @date: 2021/1/11 16:49 */ public class HelloServiceImpl extends UnicastRemoteObject implements IRemoteHelloService { private String name; public HelloServiceImpl(String name) throws RemoteException { this.name = name; } @Override public String echo(String message) throws IOException { System.out.println("dynamic load echo"); return "echo:" + message; } @Override public Date time() throws Exception { System.out.println("dynamic load time"); return new Date(); } }
服务端
package com.jnet.rmi.dynamicLoad; import com.jnet.rmi.dynamicLoad.impl.HelloServiceImpl; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import java.rmi.RemoteException; /** * @author Xunwu Yang 2021-01-17 * @version 1.0.0 */ public class SimpleServer { public static void main(String[] args) throws RemoteException, NamingException { IHelloService helloService = new HelloServiceImpl("hello"); Context namingContext = new InitialContext(); namingContext.rebind("rmi:HelloService", helloService); } }
客户端
package com.jnet.rmi.dynamicLoad; import javax.naming.Context; import javax.naming.InitialContext; /** * @author Xunwu Yang 2021-01-17 * @version 1.0.0 */ public class SimpleClient { public static void main(String[] args) throws Exception { Context namingContext = new InitialContext(); IHelloService helloService = (IHelloService) namingContext.lookup("rmi://localhost/HelloService"); System.out.println(helloService.echo("xunwu")); System.out.println(helloService.time()); } }
在非classpath路径上运行rmiregistry
然后注册服务
远程调用
https://docs.oracle.com/javase/7/docs/technotes/guides/rmi/codebase.html