一、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